-
-
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 4 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,11 @@ 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 | ||||||||||||||||||||||||||||||||||
CTRL_OR_ALT_ACTIVE = ALT_ACTIVE | CTRL_ACTIVE | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
class _error(Exception): | ||||||||||||||||||||||||||||||||||
pass | ||||||||||||||||||||||||||||||||||
|
@@ -407,31 +412,33 @@ 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) | ||||||||||||||||||||||||||||||||||
code = VK_MAP.get(key_event.wVirtualKeyCode) | ||||||||||||||||||||||||||||||||||
if code: | ||||||||||||||||||||||||||||||||||
return Event( | ||||||||||||||||||||||||||||||||||
evt="key", data=code, raw=rec.Event.KeyEvent.uChar.UnicodeChar | ||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||
if code in ("left", "right") and (ctrlstate := key_event.dwControlKeyState) and ctrlstate & CTRL_OR_ALT_ACTIVE: | ||||||||||||||||||||||||||||||||||
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. This makes On Unix for me In the keymap I also don't see Alt-left with a special meaning: Lines 146 to 161 in f157485
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. Exactly, I didn't see anything existing that was useful for 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 would prefer all implementations (unix, windows, etc.) to have the same implementation. Please revert the changes to ALT. There might be good use for the ALT modifier, but that would be for another PR. 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. That brings up a good question, why don't we add Also, I just tested that, and it made me realize that the 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 guess adding Are you ok with the changes I made so we can resolve this conversation? |
||||||||||||||||||||||||||||||||||
code = f"ctrl {code}" | ||||||||||||||||||||||||||||||||||
return Event(evt="key", data=code, raw=key) | ||||||||||||||||||||||||||||||||||
if block: | ||||||||||||||||||||||||||||||||||
continue | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
return None | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
return Event(evt="key", data=key, raw=rec.Event.KeyEvent.uChar.UnicodeChar) | ||||||||||||||||||||||||||||||||||
if (ctrlstate := key_event.dwControlKeyState) and ctrlstate & ALT_ACTIVE: | ||||||||||||||||||||||||||||||||||
paulie4 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||
# first send meta, then send the key | ||||||||||||||||||||||||||||||||||
paulie4 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||
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 :source:`Lib/_pyrepl/windows_console.py` to support more keybindings, like the :kbd:`Control-←` and :kbd:`Control-→` word-skipping keybindings and those with meta (i.e. Alt), e.g. to ``kill-word`` or ``backward-kill-word``. | ||
eendebakpt marked this conversation as resolved.
Show resolved
Hide resolved
eendebakpt marked this conversation as resolved.
Show resolved
Hide resolved
|
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.