https://github.com/python/cpython/commit/4c6318e24ddd6a4aeb243c064055a38823a142bb
commit: 4c6318e24ddd6a4aeb243c064055a38823a142bb
branch: 3.13
author: Miss Islington (bot) <31488909+miss-isling...@users.noreply.github.com>
committer: encukou <encu...@gmail.com>
date: 2025-03-03T16:57:53+01:00
summary:

[3.13] gh-128388: pyrepl on Windows: add meta and ctrl+arrow keybindings 
(GH-128389) (GH-130500)

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.
(cherry picked from commit 688f3a0d4b94874ff6d72af3baafd8bbf911153e)

Co-authored-by: Paulie Peña <203125+paul...@users.noreply.github.com>
Co-authored-by: Hugo van Kemenade <1324225+hug...@users.noreply.github.com>
Co-authored-by: Pieter Eendebak <pieter.eende...@gmail.com>

files:
A Misc/NEWS.d/next/Library/2025-01-01-19-24-43.gh-issue-128388.8UdMz_.rst
M Lib/_pyrepl/windows_console.py

diff --git a/Lib/_pyrepl/windows_console.py b/Lib/_pyrepl/windows_console.py
index fffadd5e2ec28e..48f328e4de3ce3 100644
--- a/Lib/_pyrepl/windows_console.py
+++ b/Lib/_pyrepl/windows_console.py
@@ -102,6 +102,10 @@ def __init__(self, err: int | None, descr: str | None = 
None) -> None:
 MOVE_DOWN = "\x1b[{}B"
 CLEAR = "\x1b[H\x1b[J"
 
+# State of control keys: 
https://learn.microsoft.com/en-us/windows/console/key-event-record-str
+ALT_ACTIVE = 0x01 | 0x02
+CTRL_ACTIVE = 0x04 | 0x08
+
 
 class _error(Exception):
     pass
@@ -407,31 +411,37 @@ def get_event(self, block: bool = True) -> Event | None:
                     continue
                 return None
 
-            key = rec.Event.KeyEvent.uChar.UnicodeChar
+            key_event = rec.Event.KeyEvent
+            raw_key = key = key_event.uChar.UnicodeChar
 
-            if rec.Event.KeyEvent.uChar.UnicodeChar == "\r":
-                # Make enter make unix-like
+            if key == "\r":
+                # Make enter unix-like
                 return Event(evt="key", data="\n", raw=b"\n")
-            elif rec.Event.KeyEvent.wVirtualKeyCode == 8:
+            elif key_event.wVirtualKeyCode == 8:
                 # Turn backspace directly into the command
-                return Event(
-                    evt="key",
-                    data="backspace",
-                    raw=rec.Event.KeyEvent.uChar.UnicodeChar,
-                )
-            elif rec.Event.KeyEvent.uChar.UnicodeChar == "\x00":
+                key = "backspace"
+            elif key == "\x00":
                 # Handle special keys like arrow keys and translate them into 
the appropriate command
-                code = VK_MAP.get(rec.Event.KeyEvent.wVirtualKeyCode)
-                if code:
-                    return Event(
-                        evt="key", data=code, 
raw=rec.Event.KeyEvent.uChar.UnicodeChar
-                    )
+                key = VK_MAP.get(key_event.wVirtualKeyCode)
+                if key:
+                    if key_event.dwControlKeyState & CTRL_ACTIVE:
+                        key = f"ctrl {key}"
+                    elif key_event.dwControlKeyState & ALT_ACTIVE:
+                        # queue the key, return the meta command
+                        self.event_queue.insert(0, Event(evt="key", data=key, 
raw=key))
+                        return Event(evt="key", data="\033")  # keymap.py uses 
this for meta
+                    return Event(evt="key", data=key, raw=key)
                 if block:
                     continue
 
                 return None
 
-            return Event(evt="key", data=key, 
raw=rec.Event.KeyEvent.uChar.UnicodeChar)
+            if key_event.dwControlKeyState & ALT_ACTIVE:
+                # queue the key, return the meta command
+                self.event_queue.insert(0, Event(evt="key", data=key, 
raw=raw_key))
+                return Event(evt="key", data="\033")  # keymap.py uses this 
for meta
+
+            return Event(evt="key", data=key, raw=raw_key)
 
     def push_char(self, char: int | bytes) -> None:
         """
diff --git 
a/Misc/NEWS.d/next/Library/2025-01-01-19-24-43.gh-issue-128388.8UdMz_.rst 
b/Misc/NEWS.d/next/Library/2025-01-01-19-24-43.gh-issue-128388.8UdMz_.rst
new file mode 100644
index 00000000000000..5bef0fd6bcac17
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-01-01-19-24-43.gh-issue-128388.8UdMz_.rst
@@ -0,0 +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``.

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to