Lib/PSReadLine/PSReadLine-Config.ps1
using namespace System.Management.Automation using namespace System.Management.Automation.Language using namespace Microsoft.PowerShell if (-not ($psISE)) { if ($Host.PrivateData.ToString() -eq 'Microsoft.PowerShell.Host.ISE.ISEOptions') { return $false } if (-not (Get-Module -Name PSReadLine -ErrorAction Ignore)) { Import-Module -Name PSReadLine -Global -PassThru:$false -ErrorAction Stop } Set-PSReadLineOption -PredictionSource History if ($PSVersionTable.PSEdition -eq 'Core') { try { Import-Module -Global -Name Az.Tools.Predictor -PassThru:$false -ErrorAction Stop } catch [System.IO.FileNotFoundException] { Write-Warning -Message 'Az.Tools.Predictor - Has not been installed yet.' Write-Warning -Message 'Attempting to install it now.' Find-Module -Name Az.Accounts,Az.Tools.Predictor | Install-Module -Scope AllUsers -Force -PassThru:$false Import-Module -Global -Name Az.Tools.Predictor -PassThru:$false -ErrorAction Stop } try { Set-PSReadLineOption -PredictionSource HistoryAndPlugin -ErrorAction Stop } catch { [System.Management.Automation.ErrorRecord]$e = $_ [PSCustomObject]@{ Exception = $e.Exception.Message Reason = $e.CategoryInfo.Reason Target = $e.CategoryInfo.TargetName Script = $e.InvocationInfo.ScriptName Message = $e.InvocationInfo.PositionMessage } Set-PSReadLineOption -PredictionSource History -ErrorAction Stop } } Set-PSReadLineOption -Colors @{ Selection = "$([char]27)[92;7m" InLinePrediction = "$([char]27)[36;7;238m" ListPrediction = '#1aa75a' } Set-PSReadLineOption -PromptText ( # Good ('{0}[0m> ' -f ([char]27)), # Error ('{0}[38;5;196m> {0}[0m' -f ([char]27)) ) Set-PSReadLineOption -EditMode Windows Set-PSReadLineOption -ShowToolTips Set-PSReadLineOption -ContinuationPrompt ' ' Set-PSReadLineOption -PredictionViewStyle ListView Set-PSReadLineOption -HistorySaveStyle SaveIncrementally Set-PSReadLineOption -HistorySearchCursorMovesToEnd Set-PSReadLineKeyHandler -Key UpArrow -Function HistorySearchBackward Set-PSReadLineKeyHandler -Key DownArrow -Function HistorySearchForward Set-PSReadLineKeyHandler -Chord Ctrl+b -Function BackwardWord Set-PSReadLineKeyHandler -Chord Ctrl+f -Function ForwardWord Set-PSReadLineKeyHandler -Chord 'Ctrl+d,Ctrl+c' -Function CaptureScreen ####################################################### Add-Type -AssemblyName Microsoft.PowerShell.PSReadLine2 Set-PSReadLineKeyHandler -Key '"',"'" -BriefDescription SmartInsertQuote -LongDescription 'Insert paired quotes if not already on a quote' -ScriptBlock { param($Key, $Arg) $Quote = $Key.KeyChar $SelectionStart = $null $SelectionLength = $null [PSConsoleReadLine]::GetSelectionState([ref]$SelectionStart, [ref]$SelectionLength) $Line = $null $Cursor = $null [PSConsoleReadLine]::GetBufferState([ref]$Line, [ref]$Cursor) # If text is selected, just quote it without any smarts if ($SelectionStart -ne -1) { [PSConsoleReadLine]::Replace($SelectionStart, $SelectionLength, $Quote + $Line.SubString($SelectionStart, $SelectionLength) + $Quote) [PSConsoleReadLine]::SetCursorPosition($SelectionStart + $SelectionLength + 2) return } $Ast = $null $Tokens = $null $ParseErrors = $null [PSConsoleReadLine]::GetBufferState([ref]$Ast, [ref]$Tokens, [ref]$ParseErrors, [ref]$null) function FindToken { param($Tokens, $Cursor) foreach ($Token in $Tokens) { if ($Cursor -lt $Token.Extent.StartOffset) { continue } if ($Cursor -lt $Token.Extent.EndOffset) { $Result = $Token $Token = $Token -as [StringExpandableToken] if ($Token) { $Nested = FindToken -Tokens $Token.NestedTokens -Cursor $Cursor if ($Nested) { $Result = $Nested } } return $Result } } return $null } $Token = FindToken -Tokens $Tokens -Cursor $Cursor # If we're on or inside a **quoted** string token (so not generic), we need to be smarter if ($Token -is [StringToken] -and $Token.Kind -ne [TokenKind]::Generic) { # If we're at the start of the string, assume we're inserting a new string if ($Token.Extent.StartOffset -eq $Cursor) { [PSConsoleReadLine]::Insert("$Quote$Quote ") [PSConsoleReadLine]::SetCursorPosition($Cursor + 1) return } # If we're at the end of the string, move over the closing quote if present. if ($Token.Extent.EndOffset -eq ($Cursor + 1) -and $Line[$Cursor] -eq $Quote) { [PSConsoleReadLine]::SetCursorPosition($Cursor + 1) return } } if ($null -eq $Token -or $Token.Kind -eq [TokenKind]::RParen -or $Token.Kind -eq [TokenKind]::RCurly -or $Token.Kind -eq [TokenKind]::RBracket) { if ($Line[0..$Cursor].Where{$_ -eq $Quote}.Count % 2 -eq 1) { # Odd number of quotes before the cursor, insert a single quote [PSConsoleReadLine]::Insert($Quote) } else { # Insert matching quotes, move cursor to be in between the quotes [PSConsoleReadLine]::Insert("$Quote$Quote") [PSConsoleReadLine]::SetCursorPosition($Cursor + 1) } return } # If cursor is at the start of a token, enclose it in quotes. if ($Token.Extent.StartOffset -eq $Cursor) { if ($Token.Kind -eq [TokenKind]::Generic -or $Token.Kind -eq [TokenKind]::Identifier -or $Token.Kind -eq [TokenKind]::Variable -or $Token.TokenFlags.hasFlag([TokenFlags]::Keyword)) { $End = $Token.Extent.EndOffset $Len = $End - $Cursor [PSConsoleReadLine]::Replace($Cursor, $Len, $Quote + $Line.SubString($Cursor, $Len) + $Quote) [PSConsoleReadLine]::SetCursorPosition($End + 2) return } } # We failed to be smart, so just insert a single quote [PSConsoleReadLine]::Insert($Quote) } ####################################################### Set-PSReadLineKeyHandler -Key '(','{','[' -BriefDescription InsertPairedBraces -LongDescription 'Insert matching braces' -ScriptBlock { param($Key, $Arg) $CloseChar = switch ($Key.KeyChar) { <#case#> '(' { [char]')'; break } <#case#> '{' { [char]'}'; break } <#case#> '[' { [char]']'; break } } $SelectionStart = $null $SelectionLength = $null [PSConsoleReadLine]::GetSelectionState([ref]$SelectionStart, [ref]$SelectionLength) $Line = $null $Cursor = $null [PSConsoleReadLine]::GetBufferState([ref]$Line, [ref]$Cursor) if ($SelectionStart -ne -1) { # Text is selected, wrap it in brackets [PSConsoleReadLine]::Replace($SelectionStart, $SelectionLength, $Key.KeyChar + $Line.SubString($SelectionStart, $SelectionLength) + $CloseChar) [PSConsoleReadLine]::SetCursorPosition($SelectionStart + $SelectionLength + 2) } else { # No text is selected, insert a pair [PSConsoleReadLine]::Insert("$($Key.KeyChar)$CloseChar") [PSConsoleReadLine]::SetCursorPosition($Cursor + 1) } } ####################################################### # F1 for help on the command line - naturally Set-PSReadLineKeyHandler -Key F1 -BriefDescription CommandHelp -LongDescription 'Open the help window for the current command' -ScriptBlock { [CmdletBinding()] param($Key, $Arg) $Ast = $null $Tokens = $null $Errors = $null $Cursor = $null [PSConsoleReadLine]::GetBufferState([ref]$Ast, [ref]$Tokens, [ref]$Errors, [ref]$Cursor) $CommandAst = $Ast.FindAll({ $Node = $Args[0] $Node -is [CommandAst] -and $Node.Extent.StartOffset -le $Cursor -and $Node.Extent.EndOffset -ge $Cursor }, $true) | Select-Object -Last 1 if ($CommandAst -ne $null) { $CommandName = $CommandAst.GetCommandName() if ($CommandName -ne $null) { $Command = $ExecutionContext.InvokeCommand.GetCommand($CommandName, 'All') if ($Command -is [AliasInfo]) { $CommandName = $Command.ResolvedCommandName } if ($CommandName -ne $null) { try { $null = Get-Command -Name $CommandName -ErrorAction Stop Get-Help -Name $CommandName -ShowWindow } catch { [System.Management.Automation.ErrorRecord]$e = $_ [PSCustomObject]@{ Type = $e.Exception.GetType().FullName Exception = $e.Exception.Message Reason = $e.CategoryInfo.Reason Target = $e.CategoryInfo.TargetName Script = $e.InvocationInfo.ScriptName Message = $e.InvocationInfo.PositionMessage } } } } } } ####################################################### # Ctrl + e - Replace all aliases with the full command Set-PSReadlineKeyHandler -Chord 'Ctrl+e' -Description 'Replace all aliases with the full command' -BriefDescription 'ExpandAliases' -ScriptBlock { $Ast = $null $Tokens = $null $Errors = $null $Cursor = $null [PSConsoleReadLine]::GetBufferState( [ref]$Ast, [ref]$Tokens, [ref]$Errors, [ref]$Cursor ) $StartAdjustment = 0 foreach ($Token in $Tokens) { if ($Token.TokenFlags -band [TokenFlags]::CommandName) { $Alias = $ExecutionContext.InvokeCommand.GetCommand($Token.Extent.Text, 'Alias') if ($Alias -ne $null) { $ResolvedCommand = $Alias.Definition if ($ResolvedCommand -ne $null) { $Extent = $Token.Extent $Length = $Extent.EndOffset - $Extent.StartOffset [PSConsoleReadLine]::Replace( $Extent.StartOffset + $StartAdjustment, $Length, $ResolvedCommand ) $StartAdjustment += ($ResolvedCommand.Length - $Length) } } } } } ####################################################### Set-PSReadLineKeyHandler -Chord Ctrl+w -ScriptBlock { [Microsoft.PowerShell.PSConsoleReadLine]::Insert('| Where-Object {$_. }') [Microsoft.PowerShell.PSConsoleReadLine]::BackwardChar() [Microsoft.PowerShell.PSConsoleReadLine]::BackwardChar() } ####################################################### } |