Detailed changes
@@ -2588,7 +2588,7 @@ impl Editor {
|| binding
.keystrokes()
.first()
- .is_some_and(|keystroke| keystroke.display_modifiers.modified())
+ .is_some_and(|keystroke| keystroke.modifiers().modified())
}))
}
@@ -7686,16 +7686,16 @@ impl Editor {
.keystroke()
{
modifiers_held = modifiers_held
- || (&accept_keystroke.display_modifiers == modifiers
- && accept_keystroke.display_modifiers.modified());
+ || (accept_keystroke.modifiers() == modifiers
+ && accept_keystroke.modifiers().modified());
};
if let Some(accept_partial_keystroke) = self
.accept_edit_prediction_keybind(true, window, cx)
.keystroke()
{
modifiers_held = modifiers_held
- || (&accept_partial_keystroke.display_modifiers == modifiers
- && accept_partial_keystroke.display_modifiers.modified());
+ || (accept_partial_keystroke.modifiers() == modifiers
+ && accept_partial_keystroke.modifiers().modified());
}
if modifiers_held {
@@ -9044,7 +9044,7 @@ impl Editor {
let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
- let modifiers_color = if accept_keystroke.display_modifiers == window.modifiers() {
+ let modifiers_color = if *accept_keystroke.modifiers() == window.modifiers() {
Color::Accent
} else {
Color::Muted
@@ -9056,19 +9056,19 @@ impl Editor {
.font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
.text_size(TextSize::XSmall.rems(cx))
.child(h_flex().children(ui::render_modifiers(
- &accept_keystroke.display_modifiers,
+ accept_keystroke.modifiers(),
PlatformStyle::platform(),
Some(modifiers_color),
Some(IconSize::XSmall.rems().into()),
true,
)))
.when(is_platform_style_mac, |parent| {
- parent.child(accept_keystroke.display_key.clone())
+ parent.child(accept_keystroke.key().to_string())
})
.when(!is_platform_style_mac, |parent| {
parent.child(
Key::new(
- util::capitalize(&accept_keystroke.display_key),
+ util::capitalize(accept_keystroke.key()),
Some(Color::Default),
)
.size(Some(IconSize::XSmall.rems().into())),
@@ -9249,7 +9249,7 @@ impl Editor {
accept_keystroke.as_ref(),
|el, accept_keystroke| {
el.child(h_flex().children(ui::render_modifiers(
- &accept_keystroke.display_modifiers,
+ accept_keystroke.modifiers(),
PlatformStyle::platform(),
Some(Color::Default),
Some(IconSize::XSmall.rems().into()),
@@ -9319,7 +9319,7 @@ impl Editor {
.child(completion),
)
.when_some(accept_keystroke, |el, accept_keystroke| {
- if !accept_keystroke.display_modifiers.modified() {
+ if !accept_keystroke.modifiers().modified() {
return el;
}
@@ -9338,7 +9338,7 @@ impl Editor {
.font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
.when(is_platform_style_mac, |parent| parent.gap_1())
.child(h_flex().children(ui::render_modifiers(
- &accept_keystroke.display_modifiers,
+ accept_keystroke.modifiers(),
PlatformStyle::platform(),
Some(if !has_completion {
Color::Muted
@@ -638,7 +638,7 @@ mod tests {
fn assert_bindings(keymap: &Keymap, action: &dyn Action, expected: &[&str]) {
let actual = keymap
.bindings_for_action(action)
- .map(|binding| binding.keystrokes[0].inner.unparse())
+ .map(|binding| binding.keystrokes[0].inner().unparse())
.collect::<Vec<_>>();
assert_eq!(actual, expected, "{:?}", action);
}
@@ -57,7 +57,7 @@ impl KeyBinding {
.split_whitespace()
.map(|source| {
let keystroke = Keystroke::parse(source)?;
- Ok(KeybindingKeystroke::new(
+ Ok(KeybindingKeystroke::new_with_mapper(
keystroke,
use_key_equivalents,
keyboard_mapper,
@@ -36,11 +36,13 @@ pub struct Keystroke {
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct KeybindingKeystroke {
/// The GPUI representation of the keystroke.
- pub inner: Keystroke,
+ inner: Keystroke,
/// The modifiers to display.
- pub display_modifiers: Modifiers,
+ #[cfg(target_os = "windows")]
+ display_modifiers: Modifiers,
/// The key to display.
- pub display_key: String,
+ #[cfg(target_os = "windows")]
+ display_key: String,
}
/// Error type for `Keystroke::parse`. This is used instead of `anyhow::Error` so that Zed can use
@@ -262,8 +264,17 @@ impl Keystroke {
}
impl KeybindingKeystroke {
- /// Create a new keybinding keystroke from the given keystroke
- pub fn new(
+ #[cfg(target_os = "windows")]
+ pub(crate) fn new(inner: Keystroke, display_modifiers: Modifiers, display_key: String) -> Self {
+ KeybindingKeystroke {
+ inner,
+ display_modifiers,
+ display_key,
+ }
+ }
+
+ /// Create a new keybinding keystroke from the given keystroke using the given keyboard mapper.
+ pub fn new_with_mapper(
inner: Keystroke,
use_key_equivalents: bool,
keyboard_mapper: &dyn PlatformKeyboardMapper,
@@ -271,19 +282,95 @@ impl KeybindingKeystroke {
keyboard_mapper.map_key_equivalent(inner, use_key_equivalents)
}
- pub(crate) fn from_keystroke(keystroke: Keystroke) -> Self {
- let key = keystroke.key.clone();
- let modifiers = keystroke.modifiers;
- KeybindingKeystroke {
- inner: keystroke,
- display_modifiers: modifiers,
- display_key: key,
+ /// Create a new keybinding keystroke from the given keystroke, without any platform-specific mapping.
+ pub fn from_keystroke(keystroke: Keystroke) -> Self {
+ #[cfg(target_os = "windows")]
+ {
+ let key = keystroke.key.clone();
+ let modifiers = keystroke.modifiers;
+ KeybindingKeystroke {
+ inner: keystroke,
+ display_modifiers: modifiers,
+ display_key: key,
+ }
+ }
+ #[cfg(not(target_os = "windows"))]
+ {
+ KeybindingKeystroke { inner: keystroke }
+ }
+ }
+
+ /// Returns the GPUI representation of the keystroke.
+ pub fn inner(&self) -> &Keystroke {
+ &self.inner
+ }
+
+ /// Returns the modifiers.
+ ///
+ /// Platform-specific behavior:
+ /// - On macOS and Linux, this modifiers is the same as `inner.modifiers`, which is the GPUI representation of the keystroke.
+ /// - On Windows, this modifiers is the display modifiers, for example, a `ctrl-@` keystroke will have `inner.modifiers` as
+ /// `Modifiers::control()` and `display_modifiers` as `Modifiers::control_shift()`.
+ pub fn modifiers(&self) -> &Modifiers {
+ #[cfg(target_os = "windows")]
+ {
+ &self.display_modifiers
+ }
+ #[cfg(not(target_os = "windows"))]
+ {
+ &self.inner.modifiers
}
}
+ /// Returns the key.
+ ///
+ /// Platform-specific behavior:
+ /// - On macOS and Linux, this key is the same as `inner.key`, which is the GPUI representation of the keystroke.
+ /// - On Windows, this key is the display key, for example, a `ctrl-@` keystroke will have `inner.key` as `@` and `display_key` as `2`.
+ pub fn key(&self) -> &str {
+ #[cfg(target_os = "windows")]
+ {
+ &self.display_key
+ }
+ #[cfg(not(target_os = "windows"))]
+ {
+ &self.inner.key
+ }
+ }
+
+ /// Sets the modifiers. On Windows this modifies both `inner.modifiers` and `display_modifiers`.
+ pub fn set_modifiers(&mut self, modifiers: Modifiers) {
+ self.inner.modifiers = modifiers;
+ #[cfg(target_os = "windows")]
+ {
+ self.display_modifiers = modifiers;
+ }
+ }
+
+ /// Sets the key. On Windows this modifies both `inner.key` and `display_key`.
+ pub fn set_key(&mut self, key: String) {
+ #[cfg(target_os = "windows")]
+ {
+ self.display_key = key.clone();
+ }
+ self.inner.key = key;
+ }
+
/// Produces a representation of this key that Parse can understand.
pub fn unparse(&self) -> String {
- unparse(&self.display_modifiers, &self.display_key)
+ #[cfg(target_os = "windows")]
+ {
+ unparse(&self.display_modifiers, &self.display_key)
+ }
+ #[cfg(not(target_os = "windows"))]
+ {
+ unparse(&self.inner.modifiers, &self.inner.key)
+ }
+ }
+
+ /// Removes the key_char
+ pub fn remove_key_char(&mut self) {
+ self.inner.key_char = None;
}
}
@@ -350,8 +437,8 @@ impl std::fmt::Display for Keystroke {
impl std::fmt::Display for KeybindingKeystroke {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- display_modifiers(&self.display_modifiers, f)?;
- display_key(&self.display_key, f)
+ display_modifiers(self.modifiers(), f)?;
+ display_key(self.key(), f)
}
}
@@ -354,19 +354,19 @@ impl MacPlatform {
let mut mask = NSEventModifierFlags::empty();
for (modifier, flag) in &[
(
- keystroke.display_modifiers.platform,
+ keystroke.modifiers().platform,
NSEventModifierFlags::NSCommandKeyMask,
),
(
- keystroke.display_modifiers.control,
+ keystroke.modifiers().control,
NSEventModifierFlags::NSControlKeyMask,
),
(
- keystroke.display_modifiers.alt,
+ keystroke.modifiers().alt,
NSEventModifierFlags::NSAlternateKeyMask,
),
(
- keystroke.display_modifiers.shift,
+ keystroke.modifiers().shift,
NSEventModifierFlags::NSShiftKeyMask,
),
] {
@@ -379,7 +379,7 @@ impl MacPlatform {
.initWithTitle_action_keyEquivalent_(
ns_string(name),
selector,
- ns_string(key_to_native(&keystroke.display_key).as_ref()),
+ ns_string(key_to_native(keystroke.key()).as_ref()),
)
.autorelease();
if Self::os_version() >= SemanticVersion::new(12, 0, 0) {
@@ -83,11 +83,7 @@ impl PlatformKeyboardMapper for WindowsKeyboardMapper {
..keystroke.modifiers
};
- KeybindingKeystroke {
- inner: keystroke,
- display_modifiers: modifiers,
- display_key: key,
- }
+ KeybindingKeystroke::new(keystroke, modifiers, key)
}
fn get_key_equivalents(&self) -> Option<&HashMap<char, char>> {
@@ -335,9 +331,9 @@ mod tests {
key_char: None,
};
let mapped = mapper.map_key_equivalent(keystroke.clone(), true);
- assert_eq!(mapped.inner, keystroke);
- assert_eq!(mapped.display_key, "a");
- assert_eq!(mapped.display_modifiers, Modifiers::control());
+ assert_eq!(*mapped.inner(), keystroke);
+ assert_eq!(mapped.key(), "a");
+ assert_eq!(*mapped.modifiers(), Modifiers::control());
// Shifted case, ctrl-$
let keystroke = Keystroke {
@@ -346,9 +342,9 @@ mod tests {
key_char: None,
};
let mapped = mapper.map_key_equivalent(keystroke.clone(), true);
- assert_eq!(mapped.inner, keystroke);
- assert_eq!(mapped.display_key, "4");
- assert_eq!(mapped.display_modifiers, Modifiers::control_shift());
+ assert_eq!(*mapped.inner(), keystroke);
+ assert_eq!(mapped.key(), "4");
+ assert_eq!(*mapped.modifiers(), Modifiers::control_shift());
// Shifted case, but shift is true
let keystroke = Keystroke {
@@ -357,9 +353,9 @@ mod tests {
key_char: None,
};
let mapped = mapper.map_key_equivalent(keystroke, true);
- assert_eq!(mapped.inner.modifiers, Modifiers::control());
- assert_eq!(mapped.display_key, "4");
- assert_eq!(mapped.display_modifiers, Modifiers::control_shift());
+ assert_eq!(mapped.inner().modifiers, Modifiers::control());
+ assert_eq!(mapped.key(), "4");
+ assert_eq!(*mapped.modifiers(), Modifiers::control_shift());
// Windows style
let keystroke = Keystroke {
@@ -368,9 +364,9 @@ mod tests {
key_char: None,
};
let mapped = mapper.map_key_equivalent(keystroke, true);
- assert_eq!(mapped.inner.modifiers, Modifiers::control());
- assert_eq!(mapped.inner.key, "$");
- assert_eq!(mapped.display_key, "4");
- assert_eq!(mapped.display_modifiers, Modifiers::control_shift());
+ assert_eq!(mapped.inner().modifiers, Modifiers::control());
+ assert_eq!(mapped.inner().key, "$");
+ assert_eq!(mapped.key(), "4");
+ assert_eq!(*mapped.modifiers(), Modifiers::control_shift());
}
}
@@ -820,7 +820,11 @@ impl KeymapFile {
.split_whitespace()
.map(|source| {
let keystroke = Keystroke::parse(source)?;
- Ok(KeybindingKeystroke::new(keystroke, false, keyboard_mapper))
+ Ok(KeybindingKeystroke::new_with_mapper(
+ keystroke,
+ false,
+ keyboard_mapper,
+ ))
})
.collect::<Result<Vec<_>, InvalidKeystrokeError>>()
else {
@@ -830,7 +834,7 @@ impl KeymapFile {
|| !keystrokes
.iter()
.zip(target.keystrokes)
- .all(|(a, b)| a.inner.should_match(b))
+ .all(|(a, b)| a.inner().should_match(b))
{
continue;
}
@@ -1065,7 +1069,7 @@ mod tests {
keystrokes
.split(' ')
.map(|s| {
- KeybindingKeystroke::new(
+ KeybindingKeystroke::new_with_mapper(
Keystroke::parse(s).expect("Keystrokes valid"),
false,
&DummyKeyboardMapper,
@@ -14,9 +14,9 @@ use gpui::{
Action, AppContext as _, AsyncApp, Axis, ClickEvent, Context, DismissEvent, Entity,
EventEmitter, FocusHandle, Focusable, Global, IsZero,
KeyBindingContextPredicate::{And, Descendant, Equal, Identifier, Not, NotEqual, Or},
- KeyContext, KeybindingKeystroke, Keystroke, MouseButton, PlatformKeyboardMapper, Point,
- ScrollStrategy, ScrollWheelEvent, Stateful, StyledText, Subscription, Task,
- TextStyleRefinement, WeakEntity, actions, anchored, deferred, div,
+ KeyContext, KeybindingKeystroke, MouseButton, PlatformKeyboardMapper, Point, ScrollStrategy,
+ ScrollWheelEvent, Stateful, StyledText, Subscription, Task, TextStyleRefinement, WeakEntity,
+ actions, anchored, deferred, div,
};
use language::{Language, LanguageConfig, ToOffset as _};
use notifications::status_toast::{StatusToast, ToastIcon};
@@ -420,7 +420,7 @@ fn keystrokes_match_exactly(
) -> bool {
keystrokes1.len() == keystrokes2.len()
&& keystrokes1.iter().zip(keystrokes2).all(|(k1, k2)| {
- k1.inner.key == k2.inner.key && k1.inner.modifiers == k2.inner.modifiers
+ k1.inner().key == k2.inner().key && k1.inner().modifiers == k2.inner().modifiers
})
}
@@ -532,7 +532,7 @@ impl KeymapEditor {
let keystroke_query = keystroke_query
.into_iter()
- .map(|keystroke| keystroke.inner.unparse())
+ .map(|keystroke| keystroke.inner().unparse())
.collect::<Vec<String>>()
.join(" ");
@@ -606,13 +606,13 @@ impl KeymapEditor {
let query = &keystroke_query[query_cursor];
let keystroke = &keystrokes[keystroke_cursor];
let matches = query
- .inner
+ .inner()
.modifiers
- .is_subset_of(&keystroke.inner.modifiers)
- && ((query.inner.key.is_empty()
- || query.inner.key == keystroke.inner.key)
- && query.inner.key_char.as_ref().is_none_or(
- |q_kc| q_kc == &keystroke.inner.key,
+ .is_subset_of(&keystroke.inner().modifiers)
+ && ((query.inner().key.is_empty()
+ || query.inner().key == keystroke.inner().key)
+ && query.inner().key_char.as_ref().is_none_or(
+ |q_kc| q_kc == &keystroke.inner().key,
));
if matches {
found_count += 1;
@@ -2256,12 +2256,10 @@ impl KeybindingEditorModal {
let fs = self.fs.clone();
let tab_size = cx.global::<settings::SettingsStore>().json_tab_size();
- let new_keystrokes = self
- .validate_keystrokes(cx)
- .map_err(InputError::error)?
- .into_iter()
- .map(remove_key_char)
- .collect::<Vec<_>>();
+ let mut new_keystrokes = self.validate_keystrokes(cx).map_err(InputError::error)?;
+ new_keystrokes
+ .iter_mut()
+ .for_each(|ks| ks.remove_key_char());
let new_context = self.validate_context(cx).map_err(InputError::error)?;
let new_action_args = self
@@ -2454,24 +2452,6 @@ impl KeybindingEditorModal {
}
}
-fn remove_key_char(
- KeybindingKeystroke {
- inner,
- display_modifiers,
- display_key,
- }: KeybindingKeystroke,
-) -> KeybindingKeystroke {
- KeybindingKeystroke {
- inner: Keystroke {
- modifiers: inner.modifiers,
- key: inner.key,
- key_char: None,
- },
- display_modifiers,
- display_key,
- }
-}
-
impl Render for KeybindingEditorModal {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let theme = cx.theme().colors();
@@ -116,7 +116,7 @@ impl KeystrokeInput {
&& self
.keystrokes
.last()
- .is_some_and(|last| last.display_key.is_empty())
+ .is_some_and(|last| last.key().is_empty())
{
return &self.keystrokes[..self.keystrokes.len() - 1];
}
@@ -124,15 +124,11 @@ impl KeystrokeInput {
}
fn dummy(modifiers: Modifiers) -> KeybindingKeystroke {
- KeybindingKeystroke {
- inner: Keystroke {
- modifiers,
- key: "".to_string(),
- key_char: None,
- },
- display_modifiers: modifiers,
- display_key: "".to_string(),
- }
+ KeybindingKeystroke::from_keystroke(Keystroke {
+ modifiers,
+ key: "".to_string(),
+ key_char: None,
+ })
}
fn keystrokes_changed(&self, cx: &mut Context<Self>) {
@@ -258,7 +254,7 @@ impl KeystrokeInput {
self.keystrokes_changed(cx);
if let Some(last) = self.keystrokes.last_mut()
- && last.display_key.is_empty()
+ && last.key().is_empty()
&& keystrokes_len <= Self::KEYSTROKE_COUNT_MAX
{
if !self.search && !event.modifiers.modified() {
@@ -267,15 +263,14 @@ impl KeystrokeInput {
}
if self.search {
if self.previous_modifiers.modified() {
- last.display_modifiers |= event.modifiers;
- last.inner.modifiers |= event.modifiers;
+ let modifiers = *last.modifiers() | event.modifiers;
+ last.set_modifiers(modifiers);
} else {
self.keystrokes.push(Self::dummy(event.modifiers));
}
self.previous_modifiers |= event.modifiers;
} else {
- last.display_modifiers = event.modifiers;
- last.inner.modifiers = event.modifiers;
+ last.set_modifiers(event.modifiers);
return;
}
} else if keystrokes_len < Self::KEYSTROKE_COUNT_MAX {
@@ -303,10 +298,13 @@ impl KeystrokeInput {
return;
}
- let keystroke =
- KeybindingKeystroke::new(keystroke.clone(), false, cx.keyboard_mapper().as_ref());
+ let keystroke = KeybindingKeystroke::new_with_mapper(
+ keystroke.clone(),
+ false,
+ cx.keyboard_mapper().as_ref(),
+ );
if let Some(last) = self.keystrokes.last()
- && last.display_key.is_empty()
+ && last.key().is_empty()
&& (!self.search || self.previous_modifiers.modified())
{
self.keystrokes.pop();
@@ -825,7 +823,7 @@ mod tests {
input
.keystrokes
.iter()
- .map(|keystroke| keystroke.inner.clone())
+ .map(|keystroke| keystroke.inner().clone())
.collect()
});
Self::expect_keystrokes_equal(&actual, expected);
@@ -1094,7 +1092,7 @@ mod tests {
}
fn keystrokes_str(ks: &[KeybindingKeystroke]) -> String {
- ks.iter().map(|ks| ks.inner.unparse()).join(" ")
+ ks.iter().map(|ks| ks.inner().unparse()).join(" ")
}
}
}
@@ -124,7 +124,7 @@ impl RenderOnce for KeyBinding {
"KEY_BINDING-{}",
self.keystrokes
.iter()
- .map(|k| k.display_key.to_string())
+ .map(|k| k.key().to_string())
.collect::<Vec<_>>()
.join(" ")
)
@@ -165,8 +165,8 @@ pub fn render_keybinding_keystroke(
if use_text {
let element = Key::new(
keystroke_text(
- &keystroke.display_modifiers,
- &keystroke.display_key,
+ keystroke.modifiers(),
+ keystroke.key(),
platform_style,
vim_mode,
),
@@ -178,18 +178,13 @@ pub fn render_keybinding_keystroke(
} else {
let mut elements = Vec::new();
elements.extend(render_modifiers(
- &keystroke.display_modifiers,
+ keystroke.modifiers(),
platform_style,
color,
size,
true,
));
- elements.push(render_key(
- &keystroke.display_key,
- color,
- platform_style,
- size,
- ));
+ elements.push(render_key(keystroke.key(), color, platform_style, size));
elements
}
}
@@ -418,8 +413,8 @@ pub fn text_for_keybinding_keystrokes(keystrokes: &[KeybindingKeystroke], cx: &A
.iter()
.map(|keystroke| {
keystroke_text(
- &keystroke.display_modifiers,
- &keystroke.display_key,
+ keystroke.modifiers(),
+ keystroke.key(),
platform_style,
vim_enabled,
)
@@ -4738,7 +4738,7 @@ mod tests {
// and key strokes contain the given key
bindings
.into_iter()
- .any(|binding| binding.keystrokes().iter().any(|k| k.display_key == key)),
+ .any(|binding| binding.keystrokes().iter().any(|k| k.key() == key)),
"On {} Failed to find {} with key binding {}",
line,
action.name(),