SamplePSReadlineProfile.ps1
# This is an example profile for PSReadline. # # This is roughly what I use so there is some emphasis on emacs bindings, # but most of these bindings make sense in Windows mode as well. Import-Module PSReadLine Set-PSReadLineOption -EditMode Emacs # Searching for commands with up/down arrow is really handy. The # option "moves to end" is useful if you want the cursor at the end # of the line while cycling through history like it does w/o searching, # without that option, the cursor will remain at the position it was # when you used up arrow, which can be useful if you forget the exact # string you started the search on. Set-PSReadLineOption -HistorySearchCursorMovesToEnd Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward # This is an example of a macro that you might use to execute a command. # This will add the command to history. Set-PSReadlineKeyHandler -Key Ctrl+B ` -BriefDescription BuildCurrentDirectory ` -LongDescription "Build the current directory" ` -ScriptBlock { [PSConsoleUtilities.PSConsoleReadLine]::RevertLine() [PSConsoleUtilities.PSConsoleReadLine]::Insert("msbuild") [PSConsoleUtilities.PSConsoleReadLine]::AcceptLine() } # In Emacs mode - Tab is acts like in bash, but the Windows style completion # is still useful sometimes, so bind some keys so we can do both Set-PSReadlineKeyHandler -Key Ctrl+Q -Function TabCompleteNext Set-PSReadlineKeyHandler -Key Ctrl+Shift+Q -Function TabCompletePrevious # Clipboard interaction is bound by default in Windows mode, but not Emacs mode. Set-PSReadlineKeyHandler -Key Shift+Ctrl+C -Function Copy Set-PSReadlineKeyHandler -Key Ctrl+V -Function Paste # Demo mode is great for giving demos of PSReadline, Set-PSReadlineKeyHandler -Chord 'Ctrl+D,Ctrl+E' -Function EnableDemoMode Set-PSReadlineKeyHandler -Chord 'Ctrl+D,Ctrl+D' -Function DisableDemoMode # CaptureScreen is good for blog posts or email showing a transaction # of what you did when asking for help or demonstrating a technique. Set-PSReadlineKeyHandler -Chord 'Ctrl+D,Ctrl+C' -Function CaptureScreen # The built-in word movement uses character delimiters, but token based word # movement is also very useful - these are the bindings you'd use if you # prefer the token based movements bound to the normal emacs word movement # key bindings. Set-PSReadlineKeyHandler -Key Alt+D -Function ShellKillWord Set-PSReadlineKeyHandler -Key Alt+Backspace -Function ShellBackwardKillWord Set-PSReadlineKeyHandler -Key Alt+B -Function ShellBackwardWord Set-PSReadlineKeyHandler -Key Alt+F -Function ShellForwardWord Set-PSReadlineKeyHandler -Key Shift+Alt+B -Function SelectShellBackwardWord Set-PSReadlineKeyHandler -Key Shift+Alt+F -Function SelectShellForwardWord #region Smart Insert/Delete # The next four key handlers are designed to make entering matched quotes # parens, and braces a nicer experience. I'd like to include functions # in the module that do this, but this implementation still isn't as smart # as ReSharper, so I'm just providing it as a sample. Set-PSReadlineKeyHandler -Key '"',"'" ` -BriefDescription SmartInsertQuote ` -LongDescription "Insert paired quotes if not already on a quote" ` -ScriptBlock { param($key, $arg) $line = $null $cursor = $null [PSConsoleUtilities.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) if ($line[$cursor] -eq $key.KeyChar) { # Just move the cursor [PSConsoleUtilities.PSConsoleReadLine]::SetCursorPosition($cursor + 1) } else { # Insert matching quotes, move cursor to be in between the quotes [PSConsoleUtilities.PSConsoleReadLine]::Insert("$($key.KeyChar)" * 2) [PSConsoleUtilities.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) [PSConsoleUtilities.PSConsoleReadLine]::SetCursorPosition($cursor - 1) } } 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 } } [PSConsoleUtilities.PSConsoleReadLine]::Insert("$($key.KeyChar)$closeChar") $line = $null $cursor = $null [PSConsoleUtilities.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) [PSConsoleUtilities.PSConsoleReadLine]::SetCursorPosition($cursor - 1) } Set-PSReadlineKeyHandler -Key ')',']','}' ` -BriefDescription SmartCloseBraces ` -LongDescription "Insert closing brace or skip" ` -ScriptBlock { param($key, $arg) $line = $null $cursor = $null [PSConsoleUtilities.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) if ($line[$cursor] -eq $key.KeyChar) { [PSConsoleUtilities.PSConsoleReadLine]::SetCursorPosition($cursor + 1) } else { [PSConsoleUtilities.PSConsoleReadLine]::Insert("$($key.KeyChar)") } } Set-PSReadlineKeyHandler -Key Backspace ` -BriefDescription SmartBackspace ` -LongDescription "Delete previous character or matching quotes/parens/braces" ` -ScriptBlock { param($key, $arg) $line = $null $cursor = $null [PSConsoleUtilities.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) if ($cursor -gt 0) { $toMatch = $null switch ($line[$cursor]) { <#case#> '"' { $toMatch = '"'; break } <#case#> "'" { $toMatch = "'"; break } <#case#> ')' { $toMatch = '('; break } <#case#> ']' { $toMatch = '['; break } <#case#> '}' { $toMatch = '{'; break } } if ($toMatch -ne $null -and $line[$cursor-1] -eq $toMatch) { [PSConsoleUtilities.PSConsoleReadLine]::Delete($cursor - 1, 2) } else { [PSConsoleUtilities.PSConsoleReadLine]::BackwardDeleteChar($key, $arg) } } } #endregion Smart Insert/Delete # Sometimes you enter a command but realize you forgot to do something else first. # This binding will let you save that command in the history so you can recall it, # but it doesn't actually execute. It also clears the line with RevertLine so the # undo stack is reset - though redo will still reconstruct the command line. Set-PSReadlineKeyHandler -Key Alt+w ` -BriefDescription SaveInHistory ` -LongDescription "Save current line in history but do not execute" ` -ScriptBlock { param($key, $arg) $line = $null $cursor = $null [PSConsoleUtilities.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) [PSConsoleUtilities.PSConsoleReadLine]::AddToHistory($line) [PSConsoleUtilities.PSConsoleReadLine]::RevertLine() } # Insert text from the clipboard as a here string Set-PSReadlineKeyHandler -Key Ctrl+Shift+v ` -BriefDescription PasteAsHereString ` -LongDescription "Paste the clipboard text as a here string" ` -ScriptBlock { param($key, $arg) Add-Type -Assembly PresentationCore if ([System.Windows.Clipboard]::ContainsText()) { # Get clipboard text - remove trailing spaces, convert \r\n to \n, and remove the final \n. $text = ([System.Windows.Clipboard]::GetText() -replace "\p{Zs}*`r?`n","`n").TrimEnd() [PSConsoleUtilities.PSConsoleReadLine]::Insert("@'`n$text`n'@") } else { [PSConsoleUtilities.PSConsoleReadLine]::Ding() } } # Sometimes you want to get a property of invoke a member on what you've entered so far # but you need parens to do that. This binding will help by putting parens around the whole line. Set-PSReadlineKeyHandler -Key 'Alt+(' ` -BriefDescription ParenthesizeLine ` -LongDescription "Put parenthesis around the entire line and move the cursor to the end of line" ` -ScriptBlock { param($key, $arg) $line = $null $cursor = $null [PSConsoleUtilities.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) [PSConsoleUtilities.PSConsoleReadLine]::Replace(0, $line.Length, '(' + $line + ')') [PSConsoleUtilities.PSConsoleReadLine]::EndOfLine() } # Each time you press Alt+', this key handler will change the token # under or before the cursor. It will cycle through single quotes, double quotes, or # no quotes each time it is invoked. Set-PSReadlineKeyHandler -Key "Alt+'" ` -BriefDescription ToggleQuoteArgument ` -LongDescription "Toggle quotes on the argument under the cursor" ` -ScriptBlock { param($key, $arg) $ast = $null $tokens = $null $errors = $null $cursor = $null [PSConsoleUtilities.PSConsoleReadLine]::GetBufferState([ref]$ast, [ref]$tokens, [ref]$errors, [ref]$cursor) $tokenToChange = $null foreach ($token in $tokens) { $extent = $token.Extent if ($extent.StartOffset -le $cursor -and $extent.EndOffset -ge $cursor) { $tokenToChange = $token # If the cursor is at the end (it's really 1 past the end) of the previous token, # we only want to change the previous token if there is no token under the cursor if ($extent.EndOffset -eq $cursor -and $foreach.MoveNext()) { $nextToken = $foreach.Current if ($nextToken.Extent.StartOffset -eq $cursor) { $tokenToChange = $nextToken } } break } } if ($tokenToChange -ne $null) { $extent = $tokenToChange.Extent $tokenText = $extent.Text if ($tokenText[0] -eq '"' -and $tokenText[-1] -eq '"') { # Switch to no quotes $replacement = $tokenText.Substring(1, $tokenText.Length - 2) } elseif ($tokenText[0] -eq "'" -and $tokenText[-1] -eq "'") { # Switch to double quotes $replacement = '"' + $tokenText.Substring(1, $tokenText.Length - 2) + '"' } else { # Add single quotes $replacement = "'" + $tokenText + "'" } [PSConsoleUtilities.PSConsoleReadLine]::Replace( $extent.StartOffset, $tokenText.Length, $replacement) } } # This example will replace any aliases on the command line with the resolved commands. Set-PSReadlineKeyHandler -Key "Alt+%" ` -BriefDescription ExpandAliases ` -LongDescription "Replace all aliases with the full command" ` -ScriptBlock { param($key, $arg) $ast = $null $tokens = $null $errors = $null $cursor = $null [PSConsoleUtilities.PSConsoleReadLine]::GetBufferState([ref]$ast, [ref]$tokens, [ref]$errors, [ref]$cursor) $startAdjustment = 0 foreach ($token in $tokens) { if ($token.TokenFlags -band [System.Management.Automation.Language.TokenFlags]::CommandName) { $alias = $ExecutionContext.InvokeCommand.GetCommand($token.Extent.Text, 'Alias') if ($alias -ne $null) { $resolvedCommand = $alias.ResolvedCommandName if ($resolvedCommand -ne $null) { $extent = $token.Extent $length = $extent.EndOffset - $extent.StartOffset [PSConsoleUtilities.PSConsoleReadLine]::Replace( $extent.StartOffset + $startAdjustment, $length, $resolvedCommand) # Our copy of the tokens won't have been updated, so we need to # adjust by the difference in length $startAdjustment += ($resolvedCommand.Length - $length) } } } } } # F1 for help on the command line - naturally Set-PSReadlineKeyHandler -Key F1 ` -BriefDescription CommandHelp ` -LongDescription "Open the help window for the current command" ` -ScriptBlock { param($key, $arg) $ast = $null $tokens = $null $errors = $null $cursor = $null [PSConsoleUtilities.PSConsoleReadLine]::GetBufferState([ref]$ast, [ref]$tokens, [ref]$errors, [ref]$cursor) $commandAst = $ast.FindAll( { $node = $args[0] $node -is [System.Management.Automation.Language.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 [System.Management.Automation.AliasInfo]) { $commandName = $command.ResolvedCommandName } if ($commandName -ne $null) { Get-Help $commandName -ShowWindow } } } } # # Ctrl+Shift+j then type a key to mark the current directory. # Ctrj+j then the same key will change back to that directory without # needing to type cd and won't change the command line. # $global:PSReadlineMarks = @{} Set-PSReadlineKeyHandler -Key Ctrl+Shift+j ` -BriefDescription MarkDirectory ` -LongDescription "Mark the current directory" ` -ScriptBlock { param($key, $arg) $key = [Console]::ReadKey($true) $global:PSReadlineMarks[$key.KeyChar] = $pwd } Set-PSReadlineKeyHandler -Key Ctrl+j ` -BriefDescription JumpDirectory ` -LongDescription "Goto the marked directory" ` -ScriptBlock { param($key, $arg) $key = [Console]::ReadKey() $dir = $global:PSReadlineMarks[$key.KeyChar] if ($dir) { cd $dir [PSConsoleUtilities.PSConsoleReadLine]::InvokePrompt() } } Set-PSReadlineKeyHandler -Key Alt+j ` -BriefDescription ShowDirectoryMarks ` -LongDescription "Show the currently marked directories" ` -ScriptBlock { param($key, $arg) $global:PSReadlineMarks.GetEnumerator() | % { [PSCustomObject]@{Key = $_.Key; Dir = $_.Value} } | Format-Table -AutoSize | Out-Host [PSConsoleUtilities.PSConsoleReadLine]::InvokePrompt() } |