PSReadLine: "Ctrl+[" is not bondable as a key chord in PSReadLine

Environment data

AppVeyor Build: 1.0.553

PS version: 5.1.17763.316
PSReadline version: 2.0.0-beta4, **AppVeyor Build: 1.0.553**
os: 10.0.17763.1 (WinBuild.160101.0800)
PS file version: 10.0.17763.1 (WinBuild.160101.0800)

and

PS version: 6.2.0
PSReadline version: 2.0.0-beta4, **AppVeyor Build: 1.0.553**
os: 10.0.17763.1 (WinBuild.160101.0800)
PS file version: 6.2.0

Steps to reproduce or exception report

Enable PSReadLine vi-mode While in insert-mode press Ctrl+[ and prior behavior was to change to command-mode Currently, ^[ is rendered in the console and PSReadLine remains in insert-mode.

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 4
  • Comments: 16 (6 by maintainers)

Most upvoted comments

Current workaround is to find the OEM scan code for your [ key and use the name for it from System.ConsoleKey, it works for me as Ctrl+Oem4.

I can at least shed some light on why this is so broken, or really, why it’s often broken and then sometimes seemingly not broken, with no easy permanent fix. The key combination Ctrl+[ is Esc, as in, it’s supposed to send ASCII code 27 just as pressing the escape key does. Historically that key chord is one and the same as the escape key. But nowadays there’s a whole of lot of “things” in between the keyboard and PSReadLine finally receiving they key (I’m mostly talking about .NET’s own System.Console.ReadKey implementation, but also relevant are different terminal emulators). In fact on macOS and Linux, where .NET is quite a bit “newer” and understands ASCII codes fairly well, and the terminal emulators are pretty standard, you can run [Console]::ReadKey() in PowerShell, press Ctrl+[, and you’ll get:

> [Console]::ReadKey()                                                                                                      
KeyChar    Key Modifiers
-------    --- ---------
       Escape         0

So on PSReadLine’s side, setting up a binding to listen the simultaneous press of the keys Ctrl and [ probably isn’t going to work, because the keyboard driver won’t send it, it’ll send Esc. At least that behavior though is predictable and we could special case Set-PSReadLineKeyHandler -Chord 'Ctrl+[' to be understood as listening for escape.

But Windows is where the trouble especially lies, it doesn’t follow the same rules. The same experiment on an up-to-date Windows with the latest Terminal app:

> [Console]::ReadKey()

KeyChar  Key Modifiers
-------  --- ---------
       Oem4   Control

Which at least explains why binding Ctrl+Oem4 works (for most people). It should be Esc, with no modifier key, but with however .NET is implemented, and however the particular Windows terminal is behaving, this does different things!

Repeating the experiment, on Windows, but this time using a terminal in VS Code (which is based off Xterm.js) instead of the Windows Terminal app, watch:

> [System.Console]::ReadKey()
KeyChar    Key Modifiers
-------    --- ---------
       Escape         0

That’s right! And on Windows. But so inconsistent as to produce this bug. What gives? 🤷 There’s a lot of places this could be “wrong” in different ways and therefore “fixed” and it’s hard to say what’s right.

  • PSReadLine could probably treat Ctrl+Oem4 as Escape on Windows, and also interpret the binding Ctrl+[ as Esc.
  • .NET could probably treat Ctrl+Oem4 as Escape on Windows
  • Windows Terminal could probably stop sending Ctrl+Oem4 and instead send Escape (like Xterm.js and every terminal you’ll find on macOS and Linux)
  • And probably other things

We’ve been dealing with “weird” unexpected things like this due to the historical evolution of keyboard input ever since we first got Console.ReadKey() working on .NET Core so we could implement PSReadLine. It’s complicated!

This is not working in 2.1.0-beta2 ? Is the fix for this available to be installed?

PowerShell 7.0.2
Copyright (c) Microsoft Corporation. All rights reserved.

https://aka.ms/powershell
Type 'help' to get help.

> get-module

ModuleType Version    PreRelease Name                                ExportedCommands
---------- -------    ---------- ----                                ----------------
Manifest   7.0.0.0               Microsoft.PowerShell.Management     {Add-Content, Clear-Content, Clear-Item, Clear-It…
Manifest   7.0.0.0               Microsoft.PowerShell.Utility        {Add-Member, Add-Type, Clear-Variable, Compare-Ob…
Script     2.1.0      beta2      PSReadLine                          {Get-PSReadLineKeyHandler, Get-PSReadLineOption, …

> Set-PSReadLineKeyHandler -Chord 'Ctrl+[' -Function ViCommandMode
> Set-PSReadLineOption -EditMode Vi
> Set-PSReadLineKeyHandler -Chord 'Ctrl+[' -Function ViCommandMode
> ^[^[^[^[