Support bindings with the Super (Cmd/Win/Meta) modifier (#6592)

Terminals which support the enhanced keyboard protocol send events for
keys pressed with the Super modifier (Windows/Linux key or the Command
key). The only changes that are needed to support this in Helix are:

* Mapping the modifier from crossterm's KeyModifiers to Helix's
  KeyModifiers.
* Representing and parsing the modifier from the KeyEvent text
  representation.
* Documenting the ability to remap it.

When writing keybindings, use 'Meta-', 'Cmd-' or 'Win-' which are all
synonymous. For example:

    [keys.normal]
    Cmd-s = ":write"

will trigger for the Windows or Linux keys and the Command key plus 's'.
This commit is contained in:
Michael Davis 2024-12-07 21:35:14 -06:00 committed by GitHub
parent fc9968bd4b
commit 271c32f2e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 56 additions and 3 deletions

View File

@ -72,8 +72,22 @@ # create a new minor mode bound to `+`
## Special keys and modifiers
Ctrl, Shift and Alt modifiers are encoded respectively with the prefixes
`C-`, `S-` and `A-`. Special keys are encoded as follows:
Ctrl, Shift and Alt modifiers are encoded respectively with the prefixes `C-`, `S-` and `A-`.
The [Super key](https://en.wikipedia.org/wiki/Super_key_(keyboard_button)) - the Windows/Linux
key or the Command key on Mac keyboards - is also supported when using a terminal emulator that
supports the [enhanced keyboard protocol](https://github.com/helix-editor/helix/wiki/Terminal-Support#enhanced-keyboard-protocol).
The super key is encoded with prefixes `Meta-`, `Cmd-` or `Win-`. These are all synonyms for the
super modifier - binding a key with a `Win-` modifier will mean it can be used with the
Windows/Linux key or the Command key.
```toml
[keys.normal]
C-s = ":write" # Ctrl and 's' to write
Cmd-s = ":write" # Cmd or Win or Meta and 's' to write
```
Special keys are encoded as follows:
| Key name | Representation |
| --- | --- |

View File

@ -162,7 +162,12 @@ pub(crate) mod keys {
impl fmt::Display for KeyEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"{}{}{}",
"{}{}{}{}",
if self.modifiers.contains(KeyModifiers::SUPER) {
"Meta-"
} else {
""
},
if self.modifiers.contains(KeyModifiers::SHIFT) {
"S-"
} else {
@ -312,6 +317,10 @@ fn width(&self) -> usize {
if self.modifiers.contains(KeyModifiers::CONTROL) {
width += 2;
}
if self.modifiers.contains(KeyModifiers::SUPER) {
// "-Meta"
width += 5;
}
width
}
@ -413,6 +422,7 @@ fn from_str(s: &str) -> Result<Self, Self::Err> {
"S" => KeyModifiers::SHIFT,
"A" => KeyModifiers::ALT,
"C" => KeyModifiers::CONTROL,
"Meta" | "Cmd" | "Win" => KeyModifiers::SUPER,
_ => return Err(anyhow!("Invalid key modifier '{}-'", token)),
};
@ -733,6 +743,28 @@ fn parsing_modified_keys() {
modifiers: KeyModifiers::NONE
}
);
assert_eq!(
str::parse::<KeyEvent>("Meta-c").unwrap(),
KeyEvent {
code: KeyCode::Char('c'),
modifiers: KeyModifiers::SUPER
}
);
assert_eq!(
str::parse::<KeyEvent>("Win-s").unwrap(),
KeyEvent {
code: KeyCode::Char('s'),
modifiers: KeyModifiers::SUPER
}
);
assert_eq!(
str::parse::<KeyEvent>("Cmd-d").unwrap(),
KeyEvent {
code: KeyCode::Char('d'),
modifiers: KeyModifiers::SUPER
}
);
}
#[test]

View File

@ -7,6 +7,7 @@ pub struct KeyModifiers: u8 {
const SHIFT = 0b0000_0001;
const CONTROL = 0b0000_0010;
const ALT = 0b0000_0100;
const SUPER = 0b0000_1000;
const NONE = 0b0000_0000;
}
}
@ -27,6 +28,9 @@ fn from(key_modifiers: KeyModifiers) -> Self {
if key_modifiers.contains(KeyModifiers::ALT) {
result.insert(CKeyModifiers::ALT);
}
if key_modifiers.contains(KeyModifiers::SUPER) {
result.insert(CKeyModifiers::SUPER);
}
result
}
@ -48,6 +52,9 @@ fn from(val: crossterm::event::KeyModifiers) -> Self {
if val.contains(CKeyModifiers::ALT) {
result.insert(KeyModifiers::ALT);
}
if val.contains(CKeyModifiers::SUPER) {
result.insert(KeyModifiers::SUPER);
}
result
}