Skip to content

Instantly share code, notes, and snippets.

@deqyra
Last active July 2, 2024 21:39
Show Gist options
  • Save deqyra/ff9e061cf6484b14c174412b71c795ff to your computer and use it in GitHub Desktop.
Save deqyra/ff9e061cf6484b14c174412b71c795ff to your computer and use it in GitHub Desktop.
PowerShell profile script
# 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