Skip to content

Instantly share code, notes, and snippets.

@SteveL-MSFT
Last active September 21, 2023 11:07
  • Star 69 You must be signed in to star a gist
  • Fork 16 You must be signed in to fork a gist
Star You must be signed in to star a gist
Embed
What would you like to do?
PowerShell Prompt
#Requires -Version 7
# Version 1.2.13
# check if newer version
$gistUrl = "https://api.github.com/gists/a208d2bd924691bae7ec7904cab0bd8e"
$latestVersionFile = [System.IO.Path]::Combine("$HOME",'.latest_profile_version')
$versionRegEx = "# Version (?<version>\d+\.\d+\.\d+)"
if ([System.IO.File]::Exists($latestVersionFile)) {
$latestVersion = [System.IO.File]::ReadAllText($latestVersionFile)
$currentProfile = [System.IO.File]::ReadAllText($profile)
[version]$currentVersion = "0.0.0"
if ($currentProfile -match $versionRegEx) {
$currentVersion = $matches.Version
}
if ([version]$latestVersion -gt $currentVersion) {
Write-Verbose "Your version: $currentVersion" -Verbose
Write-Verbose "New version: $latestVersion" -Verbose
$choice = Read-Host -Prompt "Found newer profile, install? (Y)"
if ($choice -eq "Y" -or $choice -eq "") {
try {
$gist = Invoke-RestMethod $gistUrl -ErrorAction Stop
$gistProfile = $gist.Files."profile.ps1".Content
Set-Content -Path $profile -Value $gistProfile
Write-Verbose "Installed newer version of profile" -Verbose
. $profile
return
}
catch {
# we can hit rate limit issue with GitHub since we're using anonymous
Write-Verbose -Verbose "Was not able to access gist, try again next time"
}
}
}
}
$global:profile_initialized = $false
function prompt {
function Initialize-Profile {
$null = Start-ThreadJob -Name "Get version of `$profile from gist" -ArgumentList $gistUrl, $latestVersionFile, $versionRegEx -ScriptBlock {
param ($gistUrl, $latestVersionFile, $versionRegEx)
try {
$gist = Invoke-RestMethod $gistUrl -ErrorAction Stop
$gistProfile = $gist.Files."profile.ps1".Content
[version]$gistVersion = "0.0.0"
if ($gistProfile -match $versionRegEx) {
$gistVersion = $matches.Version
Set-Content -Path $latestVersionFile -Value $gistVersion
}
}
catch {
# we can hit rate limit issue with GitHub since we're using anonymous
Write-Verbose -Verbose "Was not able to access gist to check for newer version"
}
}
if ((Get-Module PSReadLine).Version -lt 2.2) {
throw "Profile requires PSReadLine 2.2+"
}
# setup psdrives
if ([System.IO.File]::Exists([System.IO.Path]::Combine("$HOME",'test'))) {
New-PSDrive -Root ~/test -Name Test -PSProvider FileSystem -ErrorAction Ignore > $Null
}
if (!(Test-Path repos:)) {
if (Test-Path ([System.IO.Path]::Combine("$HOME",'git'))) {
New-PSDrive -Root ~/repos -Name git -PSProvider FileSystem > $Null
}
elseif (Test-Path "d:\PowerShell") {
New-PSDrive -Root D:\ -Name git -PSProvider FileSystem > $Null
}
}
Set-PSReadLineOption -Colors @{ Selection = "`e[92;7m"; InLinePrediction = "`e[2m" } -PredictionSource HistoryAndPlugin
Set-PSReadLineKeyHandler -Chord Shift+Tab -Function MenuComplete
Set-PSReadLineKeyHandler -Chord Ctrl+b -Function BackwardWord
Set-PSReadLineKeyHandler -Chord Ctrl+f -Function ForwardWord
Set-PSReadLineKeyHandler -Chord F2 -Function SwitchPredictionView
if ($IsWindows) {
Set-PSReadLineOption -EditMode Emacs -ShowToolTips
Set-PSReadLineKeyHandler -Chord Ctrl+Shift+c -Function Copy
Set-PSReadLineKeyHandler -Chord Ctrl+Shift+v -Function Paste
}
else {
try {
Import-PSUnixTabCompletion
}
catch [System.Management.Automation.CommandNotFoundException]
{
Install-Module Microsoft.PowerShell.UnixTabCompletion -Repository PSGallery -AcceptLicense -Force
Import-PSUnixTabCompletion
}
}
# add path to dotnet global tools
$env:PATH += [System.IO.Path]::PathSeparator + [System.IO.Path]::Combine("$HOME",'.dotnet','tools')
# ensure dotnet cli is in path
$dotnet = Get-Command dotnet -CommandType Application -ErrorAction Ignore
if ($null -eq $dotnet) {
if ([System.IO.File]::Exists("$HOME/.dotnet/dotnet")){
$env:PATH += [System.IO.Path]::PathSeparator+ [System.IO.Path]::Combine("$HOME",'.dotnet')
}
}
}
if ($global:profile_initialized -ne $true) {
$global:profile_initialized = $true
Initialize-Profile
}
$currentLastExitCode = $LASTEXITCODE
$lastSuccess = $?
$color = @{
Reset = "`e[0m"
Red = "`e[31;1m"
Green = "`e[32;1m"
Yellow = "`e[33;1m"
Grey = "`e[37;0m"
White = "`e[37;1m"
Invert = "`e[7m"
RedBackground = "`e[41m"
}
# set color of PS based on success of last execution
if ($lastSuccess -eq $false) {
$lastExit = $color.Red
} else {
$lastExit = $color.Green
}
# get the execution time of the last command
$lastCmdTime = ""
$lastCmd = Get-History -Count 1
if ($null -ne $lastCmd) {
$cmdTime = $lastCmd.Duration.TotalMilliseconds
$units = "ms"
$timeColor = $color.Green
if ($cmdTime -gt 250 -and $cmdTime -lt 1000) {
$timeColor = $color.Yellow
} elseif ($cmdTime -ge 1000) {
$timeColor = $color.Red
$units = "s"
$cmdTime = $lastCmd.Duration.TotalSeconds
if ($cmdTime -ge 60) {
$units = "m"
$cmdTIme = $lastCmd.Duration.TotalMinutes
}
}
$lastCmdTime = "$($color.Grey)[$timeColor$($cmdTime.ToString("#.##"))$units$($color.Grey)]$($color.Reset) "
}
# get git branch information if in a git folder or subfolder
$gitBranch = ""
$path = Get-Location
while ($path -ne "") {
if (Test-Path ([System.IO.Path]::Combine($path,'.git'))) {
# need to do this so the stderr doesn't show up in $error
$ErrorActionPreferenceOld = $ErrorActionPreference
$ErrorActionPreference = 'Ignore'
$branch = git rev-parse --abbrev-ref --symbolic-full-name '@{u}'
$ErrorActionPreference = $ErrorActionPreferenceOld
# handle case where branch is local
if ($lastexitcode -ne 0 -or $null -eq $branch) {
$branch = git rev-parse --abbrev-ref HEAD
}
$branchColor = $color.Green
if ($branch -match "/master") {
$branchColor = $color.Red
}
$gitBranch = " $($color.Grey)[$branchColor$branch$($color.Grey)]$($color.Reset)"
break
}
$path = Split-Path -Path $path -Parent
}
# truncate the current location if too long
$currentDirectory = $executionContext.SessionState.Path.CurrentLocation.Path
$consoleWidth = [Console]::WindowWidth
$maxPath = [int]($consoleWidth / 2)
if ($currentDirectory.Length -gt $maxPath) {
$currentDirectory = "`u{2026}" + $currentDirectory.SubString($currentDirectory.Length - $maxPath)
}
# check if running dev built pwsh
$devBuild = ''
if ($PSHOME.Contains("publish")) {
$devBuild = " $($color.White)$($color.RedBackground)DevPwsh$($color.Reset)"
}
"${lastCmdTime}${currentDirectory}${gitBranch}${devBuild}`n${lastExit}PS$($color.Reset)$('>' * ($nestedPromptLevel + 1)) "
# set window title
try {
$prefix = ''
if ($isWindows) {
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$windowsPrincipal = [Security.Principal.WindowsPrincipal]::new($identity)
if ($windowsPrincipal.IsInRole("Administrators") -eq 1) {
$prefix = "Admin:"
}
}
$Host.ui.RawUI.WindowTitle = "$prefix$PWD"
} catch {
# do nothing if can't be set
}
$global:LASTEXITCODE = $currentLastExitCode
}
@danstur
Copy link

danstur commented Apr 8, 2021

@SteveL-MSFT I'm curious why you decided to use gists and the manual handling for this instead of using using git. That's how I sync my profile (although admittedly I update manually to avoid the performance hit of explicitly checking at startup)

I "borrowed" the idea of setting up custom PSDrives for common folders though. I feel stupid for never making that connection - that sounds like it should be very useful.

@corbob
Copy link

corbob commented Apr 8, 2021

why you decided to use gists and the manual handling for this instead of using using git.

@danstur, not trying to speak for Steve here, but I had the same thought. I'm thinking the reasoning is you don't need to install git to do it this way. That being said, being on a public GitHub project would allow the same thing... Would also allow extra niceties like a license file, and perhaps the version check is pulling down a small file with the version instead of the whole script 🤷‍♂️

@BigHoss
Copy link

BigHoss commented Sep 9, 2021

@SteveL-MSFT i try to implement powerline with the following line

Import-Module posh-git
Import-Module oh-my-posh

Set-PoshPrompt -Theme paradox

but i cant seem to find out how or where to correctly implement it with this script

@mattcargile
Copy link

@BigHoss , did you ever figure it out?

You would just add those options to the top of the script. The potential issue is that oh-my-posh modifies the function:\prompt so your theme would be overridden with the next declaration of function. You would have to put the code from Steve's prompt' function in the Set-PoshContext` function.

@codigoperfeito
Copy link

codigoperfeito commented Feb 22, 2022

@BigHoss I would recommend you delete the first part of the code and create ur own profile

to delete :

check if newer version
$gistUrl = "https://api.github.com/gists/a208d2bd924691bae7ec7904cab0bd8e"
$latestVersionFile = [System.IO.Path]::Combine("$HOME",'.latest_profile_version')
$versionRegEx = "# Version (?\d+.\d+.\d+)"

to create own profile(open with ur edit code):
exemple with vscode

. code $PROFILE.CurrentUserCurrentHost

and add inside:

. $env:USERPROFILE\FOLDERYOUDIRECTORYOFUSER\YOUPROFILE.ps1

ex:

. $env:USERPROFILE\.config\user_profile.ps1

and in your "YOUPROFILE.ps1" add the rest of the code, without the part I said not to put with "Module" and "theme", remembering that it is necessary to install the modules first.

my github with code:

My powershell with my theme

@janegilring
Copy link

janegilring commented Aug 5, 2023

Did this work for you without adding -Scope Global?

New-PSDrive -Root ~/repos -Name git -PSProvider FileSystem

In my setup, I had to change the scope in order to see the mapped PSDrives.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment