Skip to content

Commit 6606f63

Browse files
pyrepl on Windows: add meta and ctrl+arrow keybindings
1 parent c5438fd commit 6606f63

File tree

1 file changed

+22
-15
lines changed

1 file changed

+22
-15
lines changed

Lib/_pyrepl/windows_console.py

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ def __init__(self, err: int | None, descr: str | None = None) -> None:
102102
MOVE_DOWN = "\x1b[{}B"
103103
CLEAR = "\x1b[H\x1b[J"
104104

105+
# State of control keys: https://learn.microsoft.com/en-us/windows/console/key-event-record-str
106+
ALT_ACTIVE = 0x01 | 0x02
107+
CTRL_ACTIVE = 0x04 | 0x08
108+
CTRL_OR_ALT_ACTIVE = ALT_ACTIVE | CTRL_ACTIVE
109+
105110

106111
class _error(Exception):
107112
pass
@@ -407,31 +412,33 @@ def get_event(self, block: bool = True) -> Event | None:
407412
continue
408413
return None
409414

410-
key = rec.Event.KeyEvent.uChar.UnicodeChar
415+
key_event = rec.Event.KeyEvent
416+
raw_key = key = key_event.uChar.UnicodeChar
411417

412-
if rec.Event.KeyEvent.uChar.UnicodeChar == "\r":
413-
# Make enter make unix-like
418+
if key == "\r":
419+
# Make enter unix-like
414420
return Event(evt="key", data="\n", raw=b"\n")
415-
elif rec.Event.KeyEvent.wVirtualKeyCode == 8:
421+
elif key_event.wVirtualKeyCode == 8:
416422
# Turn backspace directly into the command
417-
return Event(
418-
evt="key",
419-
data="backspace",
420-
raw=rec.Event.KeyEvent.uChar.UnicodeChar,
421-
)
422-
elif rec.Event.KeyEvent.uChar.UnicodeChar == "\x00":
423+
key = "backspace"
424+
elif key == "\x00":
423425
# Handle special keys like arrow keys and translate them into the appropriate command
424-
code = VK_MAP.get(rec.Event.KeyEvent.wVirtualKeyCode)
426+
code = VK_MAP.get(key_event.wVirtualKeyCode)
425427
if code:
426-
return Event(
427-
evt="key", data=code, raw=rec.Event.KeyEvent.uChar.UnicodeChar
428-
)
428+
if code in ("left", "right") and (ctrlstate := key_event.dwControlKeyState) and ctrlstate & CTRL_OR_ALT_ACTIVE:
429+
code = f"ctrl {code}"
430+
return Event(evt="key", data=code, raw=key)
429431
if block:
430432
continue
431433

432434
return None
433435

434-
return Event(evt="key", data=key, raw=rec.Event.KeyEvent.uChar.UnicodeChar)
436+
if (ctrlstate := key_event.dwControlKeyState) and ctrlstate & ALT_ACTIVE:
437+
# first send meta, then send the key
438+
self.event_queue.insert(0, Event(evt="key", data=key, raw=raw_key))
439+
return Event(evt="key", data="\033") # keymap.py uses this for meta
440+
441+
return Event(evt="key", data=key, raw=raw_key)
435442

436443
def push_char(self, char: int | bytes) -> None:
437444
"""

0 commit comments

Comments
 (0)