ProfileFever.psm1
<#
.SYNOPSIS Import the profile configuration file. #> function Import-ProfileConfig { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.String] $Path ) $configs = Get-Content -Path $Path | ConvertFrom-Json # Update configurations if ($configs.Name -contains "$Env:ComputerName\$Env:Username") { $config = $configs.Where({$_.Name -eq "$Env:ComputerName\$Env:Username"})[0] } else { $config = $configs.Where({$_.Name -eq 'Default'})[0] } Write-Output $config } <# .SYNOPSIS Show the headline with information about the local system and current user. #> function Show-HostHeadline { # Get Windows version from registry. Update the object for non Windows 10 or # Windows Server 2016 systems to match the same keys. $osVersion = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' if ($null -eq $osVersion.ReleaseId) { $osVersion | Add-Member -MemberType NoteProperty -Name 'ReleaseId' -Value $osVersion.CurrentVersion } if ($null -eq $osVersion.UBR) { $osVersion | Add-Member -MemberType NoteProperty -Name 'UBR' -Value '0' } # Rename the ConsoleHost string to a nice understandable string $profileHost = $Host.Name.Replace('ConsoleHost', 'Windows PowerShell Console Host') # Get the PowerShell version depending on the edition if ($PSVersionTable.PSEdition -eq 'Core') { $psVersion = 'Version {0}' -f $PSVersionTable.PSVersion } else { $psVersion = 'Version {0}.{1} (Build {2}.{3})' -f $PSVersionTable.PSVersion.Major, $PSVersionTable.PSVersion.Minor, $PSVersionTable.PSVersion.Build, $PSVersionTable.PSVersion.Revision } $Host.UI.WriteLine(('{0}, Version {1} (Build {2}.{3})' -f $osVersion.ProductName, $osVersion.ReleaseId, $osVersion.CurrentBuildNumber, $osVersion.UBR)) $Host.UI.WriteLine(('{0}, {1}' -f $profileHost, $psVersion)) $Host.UI.WriteLine() $Host.UI.WriteLine(('{0}\{1} on {2}, Uptime {3:%d} day(s) {3:hh\:mm\:ss}' -f $Env:USERDOMAIN, $Env:USERNAME, $Env:COMPUTERNAME.ToUpper(), [System.TimeSpan]::FromMilliseconds([System.Environment]::TickCount))) $Host.UI.WriteLine() } <# .SYNOPSIS Set the console host configuration. #> function Set-ConsoleConfig { [CmdletBinding(SupportsShouldProcess = $true)] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalFunctions', '')] param ( [Parameter(Mandatory = $false)] [System.Int32] $WindowWidth, [Parameter(Mandatory = $false)] [System.Int32] $WindowHeight, [Parameter(Mandatory = $false)] [System.ConsoleColor] $ForegroundColor, [Parameter(Mandatory = $false)] [System.ConsoleColor] $BackgroundColor ) # Step 1: Window and buffer size if ($PSCmdlet.ShouldProcess('Window and Buffer', 'Change Size')) { $bufferSize = $Global:Host.UI.RawUI.BufferSize $windowSize = $Global:Host.UI.RawUI.WindowSize $bufferSize.Height = 9999 if ($PSBoundParameters.ContainsKey('WindowWidth')) { $bufferSize.Width = $WindowWidth $windowSize.Width = $WindowWidth } if ($PSBoundParameters.ContainsKey('WindowHeight')) { $windowSize.Height = $WindowHeight } $Global:Host.UI.RawUI.BufferSize = $bufferSize $Global:Host.UI.RawUI.WindowSize = $windowSize } # Step 2: Window foreground and background color if ($PSCmdlet.ShouldProcess('Color', 'Change Color')) { if ($PSBoundParameters.ContainsKey('ForegroundColor')) { $Global:Host.UI.RawUI.ForegroundColor = $ForegroundColor } if ($PSBoundParameters.ContainsKey('BackgroundColor')) { $Global:Host.UI.RawUI.BackgroundColor = $BackgroundColor } } } <# .SYNOPSIS Play the fun tool of Rick Astley. #> function Start-RickAstley { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] param () Start-Process -FilePath 'powershell.exe' -ArgumentList '-noprofile -noexit -command iex (New-Object Net.WebClient).DownloadString(''http://bit.ly/e0Mw9w'')' } <# .SYNOPSIS Add a command not found action to the list of actions. #> function Add-CommandNotFoundAction { [CmdletBinding()] param ( # Name of the command. [Parameter(Mandatory = $true)] [System.String] $CommandName, # For the remoting command, set the computer name of the target system. [Parameter(Mandatory = $true)] [Parameter(ParameterSetName = 'RemotingWithCredential')] [Parameter(ParameterSetName = 'RemotingWithVault')] [System.String] $ComputerName, # For the remoting command, set the credentials. [Parameter(Mandatory = $false, ParameterSetName = 'RemotingWithCredential')] [System.Management.Automation.PSCredential] $Credential, # For the remoting command, but only a pointer to the credential vault. [Parameter(Mandatory = $true, ParameterSetName = 'RemotingWithVault')] [System.String] $VaultTargetName, # Define a script block to execute for the command. [Parameter(Mandatory = $true, ParameterSetName = 'ScriptBlock')] [System.Management.Automation.ScriptBlock] $ScriptBlock ) $command = @{ CommandName = $CommandName } switch ($PSCmdlet.ParameterSetName) { 'RemotingWithCredential' { $command['CommandType'] = 'Remoting' $command['ComputerName'] = $ComputerName $command['Credential'] = $Credential } 'RemotingWithVault' { $command['CommandType'] = 'Remoting' $command['ComputerName'] = $ComputerName $command['VaultTargetName'] = $VaultTargetName } 'ScriptBlock' { $command['CommandType'] = 'ScriptBlock' $command['ScriptBlock'] = $ScriptBlock } } $Script:CommandNotFoundAction += [PSCustomObject] $command } <# .SYNOPSIS Unregister the command not found action callback. #> function Disable-CommandNotFoundAction { [CmdletBinding()] param () $Global:ExecutionContext.InvokeCommand.CommandNotFoundAction = $null } <# .SYNOPSIS Register the command not found action callback. #> function Enable-CommandNotFoundAction { [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalFunctions', '')] param () $Global:ExecutionContext.InvokeCommand.CommandNotFoundAction = { param ($CommandName, $CommandLookupEventArgs) foreach ($command in $Script:CommandNotFoundAction) { if ($command.CommandName -eq $CommandName) { $commandLine = (Get-PSCallStack)[1].Position.Text.Trim() switch ($command.CommandType) { 'Remoting' { $credentialSplat = @{} if ($command.Credential) { $credentialSplat['Credential'] = $command.Credential $credentialVerbose = " -Credential '{0}'" -f $command.Credential.UserName } if ($command.VaultTargetName) { $credential = Get-VaultCredential -TargetName $command.VaultTargetName $credentialSplat['Credential'] = $credential $credentialVerbose = " -Credential '{0}'" -f $credential.UserName } # Option 1: Enter Session # If no parameters were specified, just enter into a # remote session to the target system. if ($CommandName -eq $commandLine) { Write-Verbose ("Enter-PSSession -ComputerName '{0}'{1}" -f $command.ComputerName, $credentialVerbose) $CommandLookupEventArgs.StopSearch = $true $CommandLookupEventArgs.CommandScriptBlock = { $session = New-PSSession -ComputerName $command.ComputerName @credentialSplat -ErrorAction Stop if ($Host.Name -eq 'ConsoleHost') { Invoke-Command -Session $session -ErrorAction Stop -ScriptBlock { Set-Location -Path "$Env:SystemDrive\"; $PromptLabel = $Env:ComputerName.ToUpper(); $PromptIndent = $using:session.ComputerName.Length + 4; function Global:prompt { Write-Host "[$PromptLabel]" -NoNewline -ForegroundColor Cyan; "$("`b `b" * $PromptIndent) $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) " } } } Enter-PSSession -Session $session -ErrorAction Stop }.GetNewClosure() } # Option 2: Open Session # If a variable is specified as output of the command, # a new remoting session will be opened and returned. $openSessionRegex = '^\$\S+ = {0}$' -f ([System.Text.RegularExpressions.Regex]::Escape($CommandName)) if ($commandLine -match $openSessionRegex) { Write-Verbose ("New-PSSession -ComputerName '{0}'{1}" -f $command.ComputerName, $credentialVerbose) $CommandLookupEventArgs.StopSearch = $true $CommandLookupEventArgs.CommandScriptBlock = { New-PSSession -ComputerName $command.ComputerName @credentialSplat -ErrorAction Stop }.GetNewClosure() } # Option 3: Invoke Command # If a script is appended to the command, execute that # script on the remote system. if ($commandline.StartsWith($CommandName) -and $commandLine.Length -gt $CommandName.Length) { $scriptBlock = [System.Management.Automation.ScriptBlock]::Create($commandLine.Substring($CommandName.Length).Trim()) Write-Verbose ("Invoke-Command -ComputerName '{0}'{1} -ScriptBlock {{ {2} }}" -f $command.ComputerName, $credentialVerbose, $scriptBlock.ToString()) $CommandLookupEventArgs.StopSearch = $true $CommandLookupEventArgs.CommandScriptBlock = { Invoke-Command -ComputerName $command.ComputerName @credentialSplat -ScriptBlock $scriptBlock -ErrorAction Stop }.GetNewClosure() } } 'ScriptBlock' { throw 'Not implemented!' } } } } } } <# .SYNOPSIS Disable the custom prompt and restore the default prompt. #> function Disable-Prompt { [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalFunctions', '')] param () function Global:Prompt { "PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) " # .Link # http://go.microsoft.com/fwlink/?LinkID=225750 # .ExternalHelp System.Management.Automation.dll-help.xml } } <# .SYNOPSIS Disable the prompt alias recommendation output after each command. #> function Disable-PromptAlias { [CmdletBinding()] [Alias('dalias')] param () Remove-Variable -Scope Script -Name PromptAlias -ErrorAction SilentlyContinue -Force New-Variable -Scope Script -Option ReadOnly -Name PromptAlias -Value $false -Force } <# .SYNOPSIS Disable the git repository status in the prompt. #> function Disable-PromptGit { [CmdletBinding()] [Alias('dgit')] param () Remove-Variable -Scope Script -Name PromptGit -ErrorAction SilentlyContinue -Force New-Variable -Scope Script -Option ReadOnly -Name PromptGit -Value $false -Force } <# .SYNOPSIS Disable the prompt timestamp output. #> function Disable-PromptTimeSpan { [CmdletBinding()] [Alias('dtimespan')] param () Remove-Variable -Scope Script -Name PromptTimeSpan -ErrorAction SilentlyContinue -Force New-Variable -Scope Script -Option ReadOnly -Name PromptTimeSpan -Value $false -Force } <# .SYNOPSIS Enable the custom prompt by replacing the default prompt. #> function Enable-Prompt { [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalFunctions', '')] param () function Global:Prompt { if ($Script:PromptHistory -ne $MyInvocation.HistoryId) { $Script:PromptHistory = $MyInvocation.HistoryId if ($Script:PromptAlias) { Show-PromptAliasSuggestion } if ($Script:PromptTimeSpan) { Show-PromptLastCommandDuration } } $Host.UI.Write($Script:PromptColor, $Host.UI.RawUI.BackgroundColor, '[{0:dd MMM HH:mm}]' -f [DateTime]::Now) $Host.UI.Write(" $($ExecutionContext.SessionState.Path.CurrentLocation)") if ($Script:PromptGit) { Write-VcsStatus } return "`n$($MyInvocation.HistoryId.ToString().PadLeft(3, '0'))$('>' * ($NestedPromptLevel + 1)) " } } <# .SYNOPSIS Enable the prompt alias recommendation output after each command. #> function Enable-PromptAlias { [CmdletBinding()] [Alias('ealias')] param () Remove-Variable -Scope Script -Name PromptAlias -ErrorAction SilentlyContinue -Force New-Variable -Scope Script -Option ReadOnly -Name PromptAlias -Value $true -Force } <# .SYNOPSIS Enable the git repository status in the prompt. #> function Enable-PromptGit { [CmdletBinding()] [Alias('egit')] param () if ($null -eq (Get-Module -Name posh-git)) { Import-Module -Name posh-git -Force } Remove-Variable -Scope Script -Name PromptGit -ErrorAction SilentlyContinue -Force New-Variable -Scope Script -Option ReadOnly -Name PromptGit -Value $true -Force } <# .SYNOPSIS Enable the prompt timestamp output. #> function Enable-PromptTimeSpan { [CmdletBinding()] [Alias('etimespan')] param () Remove-Variable -Scope Script -Name PromptTimeSpan -ErrorAction SilentlyContinue -Force New-Variable -Scope Script -Option ReadOnly -Name PromptTimeSpan -Value $true -Force } <# .SYNOPSIS Show the alias suggestion for the latest command. #> function Show-PromptAliasSuggestion { [CmdletBinding()] param () if ($MyInvocation.HistoryId -gt 1) { $history = Get-History -Id ($MyInvocation.HistoryId - 1) $reports = @() foreach ($alias in (Get-Alias)) { if ($history.CommandLine.IndexOf($alias.ResolvedCommandName) -ne -1) { $reports += $alias } } if ($reports.Count -gt 0) { $report = $reports | Group-Object -Property 'ResolvedCommandName' | ForEach-Object { ' ' + $_.Name + ' => ' + ($_.Group -join ', ') } $Host.UI.WriteLine('Magenta', $Host.UI.RawUI.BackgroundColor, "Alias suggestions:`n" + ($report -join "`n")) } } } <# .SYNOPSIS Show the during of the last executed command. #> function Show-PromptLastCommandDuration { [CmdletBinding()] param () if ($MyInvocation.HistoryId -gt 1 -and $Host.UI.RawUI.CursorPosition.Y -gt 0) { $history = Get-History -Id ($MyInvocation.HistoryId - 1) $duration = ($history.EndExecutionTime - $history.StartExecutionTime).ToString('\ \ \ h\:mm\:ss\.ffff') # Move cursor one up and to the right to show the execution time. $position = $Host.UI.RawUI.CursorPosition $position.Y = $position.Y - 1 $position.X = $Host.UI.RawUI.WindowSize.Width - $duration.Length $Host.UI.RawUI.CursorPosition = $position $Host.UI.Write('Gray', $Host.UI.RawUI.BackgroundColor, $duration) } } <# .SYNOPSIS Disable the information output stream for the global shell. #> function Disable-Information { [CmdletBinding()] [Alias('di')] param () Set-Variable -Scope Global -Name InformationPreference -Value 'SilentlyContinue' } <# .SYNOPSIS Disable the verbose output stream for the global shell. #> function Disable-Verbose { [CmdletBinding()] [Alias('dv')] param () Set-Variable -Scope Global -Name VerbosePreference -Value 'SilentlyContinue' } <# .SYNOPSIS Enable the information output stream for the global shell. #> function Enable-Information { [CmdletBinding()] [Alias('ei')] param () Set-Variable -Scope Global -Name InformationPreference -Value 'Continue' } <# .SYNOPSIS Enable the verbose output stream for the global shell. #> function Enable-Verbose { [CmdletBinding()] [Alias('ev')] param () Set-Variable -Scope Global -Name VerbosePreference -Value 'Continue' } <# .SYNOPSIS Update the workspace configuration for Visual Studio Code which is used by the extension vscode-open-project. .LINK https://marketplace.visualstudio.com/items?itemName=svetlozarangelov.vscode-open-project #> function Update-Workspace { [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $false)] [System.String] $Path = "$HOME\Workspace", [Parameter(Mandatory = $false)] [System.String] $ProjectListPath = "$Env:AppData\Code\User\projectlist.json" ) $projectList = @{ projects = [Ordered] @{} } foreach ($group in (Get-ChildItem -Path $Path -Directory)) { foreach ($repo in (Get-ChildItem -Path $group.FullName -Directory)) { $key = '{0} \ {1}' -f $group.Name, $repo.Name $projectList.projects.Add($key, $repo.FullName) } } if ($PSCmdlet.ShouldProcess($ProjectListPath, 'Update Project List')) { $projectList | ConvertTo-Json | Set-Content -Path $ProjectListPath } } # Module profile configuration variables $Script:PromptHistory = 0 $Script:PromptAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) $Script:PromptColor = $(if($Script:PromptAdmin) { 'Red' } else { 'DarkCyan' }) $Script:PromptAlias = $false $Script:PromptTimeSpan = $false $Script:PromptGit = $false # Module command not found action variables $Script:CommandNotFoundAction = @() |