-
-
Notifications
You must be signed in to change notification settings - Fork 30.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gh-128388: pyrepl on Windows: add meta and ctrl+arrow keybindings #128389
Changes from all commits
283e7c3
d3df8a2
459b379
abe1f62
3d07c34
16f2a13
e3f6438
46b22d1
55e4fbf
d117d52
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
eendebakpt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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 | ||
Comment on lines
+431
to
+432
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know the answer. But why pass the ctrl modifier as a key f'ctrl {key}`, and the alt modifier using a special event? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, it took me a while to figure out what was going on in |
||
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 | ||
paulie4 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return Event(evt="key", data=key, raw=raw_key) | ||
|
||
def push_char(self, char: int | bytes) -> None: | ||
""" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about including the right ALT in ALT_ACTIVE. On Linux, ALT+Backspace removes a whole word, whereas AltGr+Backspace removes a single character.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lib/_pyrepl/keymap.py
doesn't distinguish between right and left (it just hasC-
andM-
), so I'm not sure that was a conscious decision that was made for the Linux code. Maybe that's actually a bug in the_pyrepl/unix_*
code?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Python 3.14 on Linux behaves differently for left Alt and right AltGr:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On my Linux, the English keyboard layout has Right Alt which generally works like Left Alt (in a terminal).
The French or Czech layout has AltGr instead, which is apparently not Meta enough, so AltGr+Backspace works like Backspace (in both pyrepl and bash).
I tried on some Windows consoles:
Doesn't look useful, so it's probably best to have Linux-like behaviour here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what you are saying. Are you saying because the existing functionality isn't useful, we should continue to make the right
Alt
key not be useful in_pyrepl
?Either way, if this is about
AltGr
, I think we have to really understand how it works in Windows... I don't have anAltGr
key, so I can't test it, but https://en.wikipedia.org/wiki/AltGr_key says, "Windows interpretsCtrl
+Alt
asAltGr
," and since the _KEY_EVENT_RECORD doc doesn't show adwControlKeyState
value forAltGr
, doesn't that mean thatRIGHT_ALT_PRESSED
is not the same thing asAltGr
and that theLib/_pyrepl/windows_console.py
code would actually have to look for a combination ofCtrl
(left/right) andAlt
(left/right) (or maybe just lefts?) being pressed to know ifAltGr
is being pressed, or am I misunderstanding how the_KEY_EVENT_RECORD
works?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't know that other keyboards have a right ALT key different than AltGr. In this case, I'm fine with
ALT_ACTIVE = 0x01 | 0x02
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for being unclear!
I meant that the behaviour of Windows terminals I tried (cmd & powershell) is not useful, so Python should not emulate that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a feature of the keyboard layout, not the physical keyboard. To test it you can add, for example, French "AZERTY" in system settings.