Rebind Escape in PSReadline for Vi-Mode Rebind Escape in PSReadline for Vi-Mode powershell powershell

Rebind Escape in PSReadline for Vi-Mode


To achieve this, put the following in your $Profile:

Set-PSReadLineKeyHandler -Chord 'j' -ScriptBlock {  if ([Microsoft.PowerShell.PSConsoleReadLine]::InViInsertMode()) {    $key = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")    if ($key.Character -eq 'k') {      [Microsoft.PowerShell.PSConsoleReadLine]::ViCommandMode()    }    else {      [Microsoft.Powershell.PSConsoleReadLine]::Insert('j')      [Microsoft.Powershell.PSConsoleReadLine]::Insert($key.Character)    }  }}

This may cause problems with pasting 'j', however.


I'm surprised this question isn't more popular.The issue with Corben's answer is that, after pressing 'j', if the next key pressed is return or a modifier like ctrl, a literal is inserted instead of the key you would expect being used.

I've re-written the answer to fix these two problems, and also turned it into a function to make it easier to re-use (for example when binding two different letters, like jk).

Set-PSReadLineKeyHandler -vimode insert -Chord "k" -ScriptBlock { mapTwoLetterNormal 'k' 'j' }Set-PSReadLineKeyHandler -vimode insert -Chord "j" -ScriptBlock { mapTwoLetterNormal 'j' 'k' }function mapTwoLetterNormal($a, $b){  mapTwoLetterFunc $a $b -func $function:setViCommandMode}function setViCommandMode{    [Microsoft.PowerShell.PSConsoleReadLine]::ViCommandMode()}function mapTwoLetterFunc($a,$b,$func) {  if ([Microsoft.PowerShell.PSConsoleReadLine]::InViInsertMode()) {    $key = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")    if ($key.Character -eq $b) {        &$func    } else {      [Microsoft.Powershell.PSConsoleReadLine]::Insert("$a")      # Representation of modifiers (like shift) when ReadKey uses IncludeKeyDown      if ($key.Character -eq 0x00) {        return      } else {        # Insert func above converts escape characters to their literals, e.g.        # converts return to ^M. This doesn't.        $wshell = New-Object -ComObject wscript.shell        $wshell.SendKeys("{$($key.Character)}")      }    }  }}# Bonus examplefunction replaceWithExit {    [Microsoft.PowerShell.PSConsoleReadLine]::BackwardKillLine()    [Microsoft.PowerShell.PSConsoleReadLine]::KillLine()    [Microsoft.PowerShell.PSConsoleReadLine]::Insert('exit')}Set-PSReadLineKeyHandler -Chord ";" -ScriptBlock { mapTwoLetterFunc ';' 'q' -func $function:replaceWithExit }