8000 gh-128388: pyrepl on Windows: add meta and ctrl+arrow keybindings (GH… · python/cpython@688f3a0 · GitHub
[go: up one dir, main page]

Skip to content

Commit 688f3a0

Browse files
paulie4hugovkeendebakpt
authored
gh-128388: pyrepl on Windows: add meta and ctrl+arrow keybindings (GH-128389)
Fix `Lib/_pyrepl/windows_console.py` to support more keybindings, like the `Ctrl`+`←` and `Ctrl`+`→` word-skipping keybindings and those with meta (i.e. Alt), e.g. to `kill-word` or `backward-kill-word`. Specifics: if Ctrl is pressed, emit "ctrl left" and "ctrl right" instead of just "left" or "right," and if Meta/Alt is pressed, emit the special key code for meta before emitting the other key that was pressed. Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Pieter Eendebak <pieter.eendebak@gmail.com>
1 parent baf6571 commit 688f3a0

File tree

2 files changed

+27
-16
lines changed

2 files changed

+27
-16
lines changed

Lib/_pyrepl/windows_console.py

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ def __init__(self, err: int | None, descr: str | None = None) -> None:
102102< 8000 div class="diff-text-inner">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+
105109

106110
class _error(Exception):
107111
pass
@@ -407,31 +411,37 @@ def get_event(self, block: bool = True) -> Event | None:
407411
continue
408412
return None
409413

410-
key = rec.Event.KeyEvent.uChar.UnicodeChar
414+
key_event = rec.Event.KeyEvent
415+
raw_key = key = key_event.uChar.UnicodeChar
411416

412-
if rec.Event.KeyEvent.uChar.UnicodeChar == "\r":
413-
# Make enter make unix-like
417+
if key == "\r":
418+
# Make enter unix-like
414419
return Event(evt="key", data="\n", raw=b"\n")
415-
elif rec.Event.KeyEvent.wVirtualKeyCode == 8:
420+
elif key_event.wVirtualKeyCode == 8:
416421
# 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":
422+
key = "backspace"
423+
elif key == "\x00":
423424
# Handle special keys like arrow keys and translate them into the appropriate command
424-
code = VK_MAP.get(rec.Event.KeyEvent.wVirtualKeyCode)
425-
if code:
426-
return Event(
427-
evt="key", data=code, raw=rec.Event.KeyEvent.uChar.UnicodeChar
428-
)
425+
key = VK_MAP.get(key_event.wVirtualKeyCode)
426+
if key:
427+
if key_event.dwControlKeyState & CTRL_ACTIVE:
428+
key = f"ctrl {key}"
429+
elif key_event.dwControlKeyState & ALT_ACTIVE:
430+
# queue the key, return the meta command
431+
self.event_queue.insert(0, Event(evt="key", data=key, raw=key))
432+
return Event(evt="key", data="\033") # keymap.py uses this for meta
433+
return Event(evt="key", data=key, raw=key)
429434
if block:
430435
continue
431436

432437
return None
433438

434-
return Event(evt="key", data=key, raw=rec.Event.KeyEvent.uChar.UnicodeChar)
439+
if key_event.dwControlKeyState & ALT_ACTIVE:
440+
# queue the key, return the meta command
441+
self.event_queue.insert(0, Event(evt="key", data=key, raw=raw_key))
442+
return Event(evt="key", data="\033") # keymap.py uses this for meta
443+
444+
return Event(evt="key", data=key, raw=raw_key)
435445

436446
def push_char(self, char: int | bytes) -> None:
437447
"""
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix ``PyREPL`` on Windows to support more keybindings, like the :kbd:`Control-` and :kbd:`Control-` word-skipping keybindings and those with meta (i.e. :kbd:`Alt`), e.g. :kbd:`Alt-d` to ``kill-word`` or :kbd:`Alt-Backspace` ``backward-kill-word``.

0 commit comments

Comments
 (0)
0