Last active
July 2, 2024 21:39
-
-
Save deqyra/ff9e061cf6484b14c174412b71c795ff to your computer and use it in GitHub Desktop.
PowerShell profile script
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
# Required variables for Oh-my-posh setup | |
$OMPExe = "$($env:LocalAppData)\Programs\oh-my-posh\bin\oh-my-posh.exe" | |
$OMPTheme = "$($env:UserProfile)\Documents\PowerShell\emodipt-custom.omp.json" | |
# Global VS setup info | |
$VsEdition = 'Community' | |
$VsSetups = @{ | |
'2022' = @{ 'path' = "C:\Program Files\Microsoft Visual Studio\2022\$VsEdition"; 'gen' = 'Visual Studio 17 2022'; }; | |
} | |
# Custom location for CMake library setup info | |
$env:CMAKE_MODULE_PATH += 'D:\Code\CMakeModules\' | |
$env:CMAKE_PREFIX_PATH += 'D:\Code\Installed\' | |
# Custom commands and useful functions | |
function mkcd() { | |
param( | |
[Parameter(Position=0)] | |
[string]$Dir | |
) | |
mkdir $Dir | |
cd $Dir | |
} | |
function Append-Tokens() { | |
param( | |
[Parameter(Mandatory = $true, ValueFromPipeline = $true)] | |
[string] $Data, | |
[Parameter(Mandatory = $true)] | |
[string] $Delimiter, | |
[parameter(Mandatory = $true)] | |
[string[]] $Values | |
) | |
if (-not $Data.EndsWith($Delimiter)) { | |
$Data += $Delimiter | |
} | |
foreach ($v in $Values) { | |
$Data += $v + $Delimiter | |
} | |
return $Data | |
} | |
function Remove-Tokens() { | |
param( | |
[Parameter(Mandatory = $true, ValueFromPipeline = $true)] | |
[string] $Data, | |
[Parameter(Mandatory = $true)] | |
[string] $Delimiter, | |
[parameter(Mandatory = $true)] | |
[string[]] $Values | |
) | |
$ValueMatch = { | |
param($ItVal) | |
$ItVal = $ItVal.Trim() | |
foreach ($v in $Values) { | |
if (($ItVal -ieq $v.Trim()) -or ($ItVal -eq '')) { | |
return $true | |
} | |
} | |
return $false | |
} | |
$sp = [System.Collections.Generic.List[System.Object]]($Data -Split ';') | |
$removed = $sp.RemoveAll($ValueMatch) | |
return $sp | Join-String -Separator $Delimiter | |
} | |
function Get-Assemblies() { | |
param($Match, [Switch]$NamesOnly); | |
$matchingAsms = [Threading.Thread]::GetDomain().GetAssemblies() | | |
? {$_.Location -match $Match } | |
if ($NamesOnly.IsPresent) { | |
return $matchingAsms | select @{Name="Name"; Expression={ (dir $_.Location).Name }} | |
} else { | |
return $matchingAsms | |
} | |
} | |
# Env setup entries | |
$DevEnvs = [ordered]@{ | |
'MSYS2' = @{ 'enabled' = $false; 'paths' = "$env:MSYS_HOME\usr\bin" ; 'var' = '$env:POSH_ENV_MSYS2'; 'c' = 'gcc' ; 'cxx' = 'g++' ; 'gen' = 'MinGW Makefiles' ; }; | |
'MinGW' = @{ 'enabled' = $false; 'paths' = "$env:MSYS_HOME\ucrt64\bin"; 'var' = '$env:POSH_ENV_MinGW'; 'c' = 'gcc' ; 'cxx' = 'g++' ; 'gen' = 'MinGW Makefiles' ; }; | |
'LLVM' = @{ 'enabled' = $false; 'paths' = "$env:LLVM_HOME\bin" ; 'var' = '$env:POSH_ENV_LLVM' ; 'c' = 'clang'; 'cxx' = 'clang++'; 'gen' = 'Ninja' ; }; | |
'LLVM2' = @{ 'enabled' = $false; 'paths' = "$env:LLVM2_HOME\bin" ; 'var' = '$env:POSH_ENV_LLVM2'; 'c' = 'clang'; 'cxx' = 'clang++'; 'gen' = 'Ninja' ; }; | |
'Hs' = @{ 'enabled' = $false; 'paths' = "$env:GHCUP_HOME\bin" ; 'var' = '$env:POSH_ENV_HS'; 'c' = 'clang'; 'cxx' = 'clang++'; 'gen' = 'Ninja' ; }; | |
} | |
# Calculate and append VC++ specific env setup entries | |
foreach ($Version in $VsSetups.Keys) { | |
$ThisVsPath = $VsSetups[$Version]['path'] | |
$ThisVcPaths = @( | |
"$ThisVsPath\\MSBuild\Current\Bin\amd64", | |
"$ThisVsPath\Common7\IDE\", | |
"$ThisVsPath\Common7\Tools\", | |
"C:\Windows\Microsoft.NET\Framework\v4.0.30319" | |
) | |
$ShortenedVersion = $Version.Substring(2, 2) | |
$VCVersion = "VC$ShortenedVersion" | |
$DevEnvs["$VCVersion"] = @{ | |
'enabled' = $false; | |
'paths' = $ThisVcPaths; | |
'var' = '$env:POSH_ENV_' + $VCVersion; | |
'c' = 'cl'; | |
'cxx' = 'cl'; | |
'gen' = $VsSetups[$Version]['gen']; | |
} | |
} | |
# Stacking dev envs capacity | |
$DevEnvPriority = $DevEnvs.keys[(@($DevEnvs.keys).length - 1)..0] | |
function Push-LastCompilersOnCMakeEnv() { | |
foreach ($Name in $DevEnvPriority) { | |
if ($DevEnvs[$Name]['enabled']) { | |
$env:CC = $DevEnvs[$Name]['c'] | |
$env:CXX = $DevEnvs[$Name]['cxx'] | |
$env:CMAKE_GENERATOR = $DevEnvs[$Name]['gen'] | |
Write-Host "Pushed compilers and generator for enabled env '$Name' on CMake env" | |
break | |
} | |
} | |
} | |
function Has-LoadedDifferentVCAssembliesBefore() { | |
param([int] $Version) | |
$DifferentAssemblies = Get-Assemblies -Match 'Microsoft.VisualStudio' | Where-Object { -not $_.Location.Contains("$Version")} | |
return $DifferentAssemblies.Count -gt 0 | |
} | |
function Enable-DevEnv() { | |
param([string] $Name) | |
if ($Name.StartsWith('VC')) { | |
$Version = 2000 + $Name.Substring(2, 2) | |
Write-Host $Version | |
if (-not ($VsSetups.Keys -contains "$Version")) { | |
Write-Error "The requested version of VC++ is not installed on the current system, or this script could not find the related setup info." | |
return | |
} | |
if (Has-LoadedDifferentVCAssembliesBefore $Version) { | |
Write-Error "A different VC++ environment was loaded in this PowerShell session prior to this one. PowerShell cannot unload assemblies once loaded, and loading additional assemblies for the requested VC++ environment would cause conflicts. PowerShell must be restarted before another VC++ environment may be loaded." | |
return | |
} | |
} | |
if (-not $DevEnvs[$Name]['enabled']) { | |
if ($Name.StartsWith('VC')) { | |
$VsPath = $VsSetups["$Version"]['path'] | |
& "$VsPath\Common7\Tools\Launch-VsDevShell.ps1" -SkipAutomaticLocation | |
} else { | |
$env:PATH = $env:PATH | Append-Tokens -Delimiter ';' -Values $($DevEnvs[$Name]['paths']) | |
} | |
if ($Name -eq 'MinGW') { | |
$env:MSYSTEM = "MINGW64" | |
Enable-DevEnv 'MSYS2' | |
} | |
if ($Name -ne 'Hs') { | |
$env:CMAKE_GENERATOR = $DevEnvs[$Name]['gen'] | |
if ($env:CMAKE_GENERATOR -eq 'Ninja') { | |
$env:CMAKE_EXPORT_COMPILE_COMMANDS = $true | |
} | |
$env:CC = $DevEnvs[$Name]['c'] | |
$env:CXX = $DevEnvs[$Name]['cxx'] | |
} | |
$DevEnvs[$Name]['enabled'] = $true | |
($DevEnvs[$Name]['var'] + ' = "1"') | Invoke-Expression | |
} | |
} | |
function Disable-DevEnv() { | |
param([string] $Name) | |
if ($DevEnvs[$Name]['enabled']) { | |
$env:PATH = $env:PATH | Remove-Tokens -Delimiter ';' -Values $DevEnvs[$Name]['paths'] | |
if ($Name.StartsWith('VC')) { | |
Remove-Module -Name 'Microsoft.VisualStudio.DevShell' | |
} | |
if ($Name -ne 'Hs') { | |
$env:CC = $null | |
$env:CXX = $null | |
if ($env:CMAKE_GENERATOR -eq 'Ninja') { | |
$env:CMAKE_EXPORT_COMPILE_COMMANDS = $null | |
} | |
$env:CMAKE_GENERATOR = $null | |
} | |
$DevEnvs[$Name]['enabled'] = $false | |
if ($Name -ne 'Hs') { | |
Push-LastCompilersOnCMakeEnv | |
} | |
if ($Name -eq 'MinGW') { | |
$env:MSYSTEM = $null | |
Disable-DevEnv 'MSYS2' | |
} | |
($DevEnvs[$Name]['var'] + ' = "0"') | Invoke-Expression | |
} | |
} | |
# region msys2 | |
function msys2() { Enable-DevEnv 'MSYS2' } | |
function unmsys2() { Disable-DevEnv 'MSYS2' } | |
#region mingw | |
function mingw() { Enable-DevEnv 'MinGW' } | |
function unmingw() { Disable-DevEnv 'MinGW' } | |
# region llvm | |
function llvm() { Enable-DevEnv 'LLVM' } | |
function unllvm() { Disable-DevEnv 'LLVM' } | |
# region llvm2 | |
function llvm2() { Enable-DevEnv 'LLVM2' } | |
function unllvm2() { Disable-DevEnv 'LLVM2' } | |
# region hs | |
function hs() { Enable-DevEnv 'Hs' } | |
function unhs() { Disable-DevEnv 'Hs' } | |
# region vc++ | |
function vc() { | |
param( | |
[string] $Version | |
) | |
Enable-DevEnv ("VC" + $Version) | |
} | |
function unvc() { | |
Disable-DevEnv 'VC22' | |
} | |
if ($null -ne (Get-Module -Name "oh-my-posh-core")) { | |
Remove-Module -Name "oh-my-posh-core" -Force | |
} | |
function global:Get-PoshStackCount { | |
$locations = Get-Location -Stack | |
if ($locations) { | |
return $locations.Count | |
} | |
return 0 | |
} | |
New-Module -Name "oh-my-posh-core" -ScriptBlock { | |
$script:ErrorCode = 0 | |
$script:ExecutionTime = 0 | |
$script:OMPExecutable = $OMPExe | |
$script:ShellName = "pwsh" | |
$script:PSVersion = $PSVersionTable.PSVersion.ToString() | |
$script:TransientPrompt = $false | |
$env:POWERLINE_COMMAND = "oh-my-posh" | |
$env:CONDA_PROMPT_MODIFIER = $false | |
$env:POSH_THEME = $OMPTheme | |
function Start-Utf8Process { | |
param( | |
[string]$FileName, | |
[string[]]$Arguments = @() | |
) | |
$Process = New-Object System.Diagnostics.Process | |
$StartInfo = $Process.StartInfo | |
$StartInfo.FileName = $FileName | |
$Arguments | ForEach-Object -Process { $StartInfo.ArgumentList.Add($_) } | |
$StartInfo.StandardErrorEncoding = $StartInfo.StandardOutputEncoding = [System.Text.Encoding]::UTF8 | |
$StartInfo.RedirectStandardError = $StartInfo.RedirectStandardInput = $StartInfo.RedirectStandardOutput = $true | |
$StartInfo.UseShellExecute = $false | |
if ($PWD.Provider.Name -eq 'FileSystem') { | |
# make sure we're in a valid directory | |
# if not, go back HOME | |
if (-not (Test-Path -LiteralPath $PWD)) { | |
Write-Host "Unable to find the current directory, falling back to $HOME" -ForegroundColor Red | |
Set-Location $HOME | |
} | |
$StartInfo.WorkingDirectory = $PWD.ProviderPath | |
} | |
$StartInfo.CreateNoWindow = $true | |
[void]$Process.Start() | |
# we do this to remove a deadlock potential on Windows | |
$stdoutTask = $Process.StandardOutput.ReadToEndAsync() | |
$stderrTask = $Process.StandardError.ReadToEndAsync() | |
$Process.WaitForExit() | |
$stderr = $stderrTask.Result.Trim() | |
if ($stderr -ne '') { | |
$Host.UI.WriteErrorLine($stderr) | |
} | |
$stdoutTask.Result | |
} | |
function Update-PoshErrorCode { | |
$lastHistory = Get-History -ErrorAction Ignore -Count 1 | |
# error code should be updated only when a non-empty command is run | |
if (($null -eq $lastHistory) -or ($script:LastHistoryId -eq $lastHistory.Id)) { | |
$script:ExecutionTime = 0 | |
return | |
} | |
$script:LastHistoryId = $lastHistory.Id | |
$script:ExecutionTime = ($lastHistory.EndExecutionTime - $lastHistory.StartExecutionTime).TotalMilliseconds | |
if ($script:OriginalLastExecutionStatus) { | |
$script:ErrorCode = 0 | |
return | |
} | |
$invocationInfo = try { | |
# retrieve info of the most recent error | |
$global:Error[0] | Where-Object { $_ -ne $null } | Select-Object -ExpandProperty InvocationInfo | |
} catch { $null } | |
# check if the last command caused the last error | |
if ($null -ne $invocationInfo -and $lastHistory.CommandLine -eq $invocationInfo.Line) { | |
$script:ErrorCode = 1 | |
return | |
} | |
if ($script:OriginalLastExitCode -is [int] -and $script:OriginalLastExitCode -ne 0) { | |
# native app exit code | |
$script:ErrorCode = $script:OriginalLastExitCode | |
return | |
} | |
} | |
function prompt { | |
# store the orignal last command execution status and last exit code | |
$script:OriginalLastExecutionStatus = $? | |
$script:OriginalLastExitCode = $global:LASTEXITCODE | |
$script:PromptType = "primary" | |
Update-PoshErrorCode | |
$cleanPSWD = $PWD.ToString().TrimEnd('\') | |
$stackCount = global:Get-PoshStackCount | |
$terminalWidth = $Host.UI.RawUI.WindowSize.Width | |
# set a sane default when the value can't be retrieved | |
if (-not $terminalWidth) { | |
$terminalWidth = 0 | |
} | |
$standardOut = @(Start-Utf8Process $script:OMPExecutable @("print", $script:PromptType, "--error=$script:ErrorCode", "--pswd=$cleanPSWD", "--execution-time=$script:ExecutionTime", "--stack-count=$stackCount", "--config=$env:POSH_THEME", "--shell-version=$script:PSVersion", "--terminal-width=$terminalWidth", "--shell=$script:ShellName")) | |
# make sure PSReadLine knows if we have a multiline prompt | |
Set-PSReadLineOption -ExtraPromptLineCount (($standardOut | Measure-Object -Line).Lines - 1) | |
# the output can be multiline, joining these ensures proper rendering by adding line breaks with `n | |
$standardOut -join "`n" | |
# restore the orignal last exit code | |
$global:LASTEXITCODE = $script:OriginalLastExitCode | |
} | |
# set secondary prompt | |
Set-PSReadLineOption -ContinuationPrompt (@(Start-Utf8Process $script:OMPExecutable @("print", "secondary", "--config=$env:POSH_THEME", "--shell=$script:ShellName")) -join "`n") | |
# perform cleanup on removal so a new initialization in current session works | |
$ExecutionContext.SessionState.Module.OnRemove += { | |
if ((Get-PSReadLineKeyHandler -Key Spacebar).Function -eq 'OhMyPoshSpaceKeyHandler') { | |
Remove-PSReadLineKeyHandler -Key Spacebar | |
} | |
if ((Get-PSReadLineKeyHandler -Key Enter).Function -eq 'OhMyPoshEnterKeyHandler') { | |
Set-PSReadLineKeyHandler -Key Enter -Function AcceptLine | |
} | |
} | |
Export-ModuleMember -Function @( | |
"Set-PoshContext" | |
"prompt" | |
) | |
} | Import-Module -Global |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment