Use lowercase for vim key bindings (#25024)

Vim itself shows keybindings cased according to the character, and so
it's confusing for people coming from vim that we show all key-bindings
uppercase with an icon denoting shift (c.f. #14287).

So, in #24322 we changed Zed to use lowercase for bindings in vim mode.
Unfortunately this (in practice) looks awful because our key icons are
designed to be paired with uppercase letters.

Instead, we now (for vim mode) show the same syntax as would be used in
the keymap file for Zed. This is different from vim (i.e. we show
shift-z instead of Z, and cmd-d instead of <D-z>) but hopefully avoids
the original confusion, and as a bonus makes it easy to figure out the
syntax you need for a workspace::SendKeystrokes.

Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...
This commit is contained in:
Conrad Irwin 2025-02-17 09:17:22 -08:00 committed by GitHub
parent e45b5cadc0
commit 08e9080ec9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -92,15 +92,7 @@ impl KeyBinding {
match key_icon {
Some(icon) => KeyIcon::new(icon, color).size(self.size).into_any_element(),
None => {
let key = if self.vim_mode {
if keystroke.modifiers.shift && keystroke.key.len() == 1 {
keystroke.key.to_ascii_uppercase().to_string()
} else {
keystroke.key.to_string()
}
} else {
util::capitalize(&keystroke.key)
};
let key = util::capitalize(&keystroke.key);
Key::new(&key, color).size(self.size).into_any_element()
}
}
@ -129,14 +121,25 @@ impl RenderOnce for KeyBinding {
.py_0p5()
.rounded_sm()
.text_color(cx.theme().colors().text_muted)
.children(render_modifiers(
&keystroke.modifiers,
self.platform_style,
None,
self.size,
true,
))
.map(|el| el.child(self.render_key(&keystroke, None)))
.when(self.vim_mode, |el| {
el.child(
Key::new(
keystroke_text(&keystroke, self.platform_style, self.vim_mode),
None,
)
.size(self.size),
)
})
.when(!self.vim_mode, |el| {
el.children(render_modifiers(
&keystroke.modifiers,
self.platform_style,
None,
self.size,
true,
))
.map(|el| el.child(self.render_key(&keystroke, None)))
})
}))
}
}
@ -359,80 +362,75 @@ pub fn text_for_keystroke(keystroke: &Keystroke, cx: &App) -> String {
}
/// Returns a textual representation of the given [`Keystroke`].
fn keystroke_text(
keystroke: &Keystroke,
platform_style: PlatformStyle,
vim_enabled: bool,
) -> String {
fn keystroke_text(keystroke: &Keystroke, platform_style: PlatformStyle, vim_mode: bool) -> String {
let mut text = String::new();
let delimiter = match platform_style {
PlatformStyle::Mac => '-',
PlatformStyle::Linux | PlatformStyle::Windows => '+',
let delimiter = match (platform_style, vim_mode) {
(PlatformStyle::Mac, false) => '-',
(PlatformStyle::Linux | PlatformStyle::Windows, false) => '+',
(_, true) => '-',
};
if keystroke.modifiers.function {
match platform_style {
PlatformStyle::Mac => text.push_str("Fn"),
PlatformStyle::Linux | PlatformStyle::Windows => text.push_str("Fn"),
match vim_mode {
false => text.push_str("Fn"),
true => text.push_str("fn"),
}
text.push(delimiter);
}
if keystroke.modifiers.control {
match platform_style {
PlatformStyle::Mac => text.push_str("Control"),
PlatformStyle::Linux | PlatformStyle::Windows => text.push_str("Ctrl"),
}
text.push(delimiter);
}
if keystroke.modifiers.alt {
match platform_style {
PlatformStyle::Mac => text.push_str("Option"),
PlatformStyle::Linux | PlatformStyle::Windows => text.push_str("Alt"),
match (platform_style, vim_mode) {
(PlatformStyle::Mac, false) => text.push_str("Control"),
(PlatformStyle::Linux | PlatformStyle::Windows, false) => text.push_str("Ctrl"),
(_, true) => text.push_str("ctrl"),
}
text.push(delimiter);
}
if keystroke.modifiers.platform {
match platform_style {
PlatformStyle::Mac => text.push_str("Command"),
PlatformStyle::Linux => text.push_str("Super"),
PlatformStyle::Windows => text.push_str("Win"),
match (platform_style, vim_mode) {
(PlatformStyle::Mac, false) => text.push_str("Command"),
(PlatformStyle::Mac, true) => text.push_str("cmd"),
(PlatformStyle::Linux, false) => text.push_str("Super"),
(PlatformStyle::Linux, true) => text.push_str("super"),
(PlatformStyle::Windows, false) => text.push_str("Win"),
(PlatformStyle::Windows, true) => text.push_str("win"),
}
text.push(delimiter);
}
if keystroke.modifiers.alt {
match (platform_style, vim_mode) {
(PlatformStyle::Mac, false) => text.push_str("Option"),
(PlatformStyle::Linux | PlatformStyle::Windows, false) => text.push_str("Alt"),
(_, true) => text.push_str("alt"),
}
text.push(delimiter);
}
if keystroke.modifiers.shift {
if !(vim_enabled && keystroke.key.len() == 1) {
match platform_style {
PlatformStyle::Mac | PlatformStyle::Linux | PlatformStyle::Windows => {
text.push_str("Shift")
}
}
text.push(delimiter);
match (platform_style, vim_mode) {
(_, false) => text.push_str("Shift"),
(_, true) => text.push_str("shift"),
}
text.push(delimiter);
}
let key = match keystroke.key.as_str() {
"pageup" => "PageUp",
"pagedown" => "PageDown",
key if vim_enabled => {
if !keystroke.modifiers.shift && key.len() == 1 {
key
} else {
&util::capitalize(key)
}
}
key => &util::capitalize(key),
};
text.push_str(key);
if vim_mode {
text.push_str(&keystroke.key)
} else {
let key = match keystroke.key.as_str() {
"pageup" => "PageUp",
"pagedown" => "PageDown",
key => &util::capitalize(key),
};
text.push_str(key);
}
text
}