Detailed changes
@@ -51,7 +51,7 @@
///
use crate::{
Action, ActionRegistry, App, BindingIndex, DispatchPhase, EntityId, FocusId, KeyBinding,
- KeyContext, Keymap, Keystroke, ModifiersChangedEvent, Window,
+ KeyContext, KeybindingKeystroke, Keymap, Keystroke, ModifiersChangedEvent, Window,
};
use collections::FxHashMap;
use smallvec::SmallVec;
@@ -444,10 +444,11 @@ impl DispatchTree {
fn binding_matches_predicate_and_not_shadowed(
keymap: &Keymap,
binding_index: BindingIndex,
- keystrokes: &[Keystroke],
+ keystrokes: &[KeybindingKeystroke],
context_stack: &[KeyContext],
) -> bool {
- let (bindings, _) = keymap.bindings_for_input_with_indices(&keystrokes, context_stack);
+ let (bindings, _) =
+ keymap.bindings_for_keybinding_keystroke_with_indices(&keystrokes, context_stack);
if let Some((highest_precedence_index, _)) = bindings.iter().next() {
binding_index == *highest_precedence_index
} else {
@@ -4,7 +4,7 @@ mod context;
pub use binding::*;
pub use context::*;
-use crate::{Action, Keystroke, is_no_action};
+use crate::{Action, KeybindingKeystroke, Keystroke, is_no_action};
use collections::HashMap;
use smallvec::SmallVec;
use std::any::TypeId;
@@ -177,10 +177,37 @@ impl Keymap {
.map(|pending| (BindingIndex(ix), binding, pending))
});
+ self.bindings_for_keystrokes_with_indices_inner(possibilities, context_stack)
+ }
+
+ /// TODO:
+ pub fn bindings_for_keybinding_keystroke_with_indices(
+ &self,
+ input: &[KeybindingKeystroke],
+ context_stack: &[KeyContext],
+ ) -> (SmallVec<[(BindingIndex, KeyBinding); 1]>, bool) {
+ let possibilities = self
+ .bindings()
+ .enumerate()
+ .rev()
+ .filter_map(|(ix, binding)| {
+ binding
+ .match_keybinding_keystrokes(input)
+ .map(|pending| (BindingIndex(ix), binding, pending))
+ });
+
+ self.bindings_for_keystrokes_with_indices_inner(possibilities, context_stack)
+ }
+
+ fn bindings_for_keystrokes_with_indices_inner<'a>(
+ &'a self,
+ possibilities: impl Iterator<Item = (BindingIndex, &'a KeyBinding, bool)>,
+ context_stack: &[KeyContext],
+ ) -> (SmallVec<[(BindingIndex, KeyBinding); 1]>, bool) {
let mut bindings: SmallVec<[(BindingIndex, KeyBinding, usize); 1]> = SmallVec::new();
// (pending, is_no_action, depth, keystrokes)
- let mut pending_info_opt: Option<(bool, bool, usize, &[Keystroke])> = None;
+ let mut pending_info_opt: Option<(bool, bool, usize, &[KeybindingKeystroke])> = None;
'outer: for (binding_index, binding, pending) in possibilities {
for depth in (0..=context_stack.len()).rev() {
@@ -2,13 +2,16 @@ use std::rc::Rc;
use collections::HashMap;
-use crate::{Action, InvalidKeystrokeError, KeyBindingContextPredicate, Keystroke, SharedString};
+use crate::{
+ Action, InvalidKeystrokeError, KeyBindingContextPredicate, KeybindingKeystroke, Keystroke,
+ SharedString,
+};
use smallvec::SmallVec;
/// A keybinding and its associated metadata, from the keymap.
pub struct KeyBinding {
pub(crate) action: Box<dyn Action>,
- pub(crate) keystrokes: SmallVec<[Keystroke; 2]>,
+ pub(crate) keystrokes: SmallVec<[KeybindingKeystroke; 2]>,
pub(crate) context_predicate: Option<Rc<KeyBindingContextPredicate>>,
pub(crate) meta: Option<KeyBindingMetaIndex>,
/// The json input string used when building the keybinding, if any
@@ -46,16 +49,17 @@ impl KeyBinding {
key_equivalents: Option<&HashMap<char, char>>,
action_input: Option<SharedString>,
) -> std::result::Result<Self, InvalidKeystrokeError> {
- let mut keystrokes: SmallVec<[Keystroke; 2]> = keystrokes
+ let mut keystrokes: SmallVec<[KeybindingKeystroke; 2]> = keystrokes
.split_whitespace()
- .map(Keystroke::parse)
+ .map(KeybindingKeystroke::parse)
.collect::<std::result::Result<_, _>>()?;
if let Some(equivalents) = key_equivalents {
for keystroke in keystrokes.iter_mut() {
- if keystroke.key.chars().count() == 1 {
- if let Some(key) = equivalents.get(&keystroke.key.chars().next().unwrap()) {
- keystroke.key = key.to_string();
+ if keystroke.inner.key.chars().count() == 1 {
+ if let Some(key) = equivalents.get(&keystroke.inner.key.chars().next().unwrap())
+ {
+ keystroke.inner.key = key.to_string();
}
}
}
@@ -96,8 +100,23 @@ impl KeyBinding {
Some(self.keystrokes.len() > typed.len())
}
+ /// TODO:
+ pub fn match_keybinding_keystrokes(&self, typed: &[KeybindingKeystroke]) -> Option<bool> {
+ if self.keystrokes.len() < typed.len() {
+ return None;
+ }
+
+ for (target, typed) in self.keystrokes.iter().zip(typed.iter()) {
+ if !typed.inner.should_match(target) {
+ return None;
+ }
+ }
+
+ Some(self.keystrokes.len() > typed.len())
+ }
+
/// Get the keystrokes associated with this binding
- pub fn keystrokes(&self) -> &[Keystroke] {
+ pub fn keystrokes(&self) -> &[KeybindingKeystroke] {
self.keystrokes.as_slice()
}
@@ -22,7 +22,8 @@ pub struct Keystroke {
}
/// TODO:
-pub struct KeybindKeystroke {
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct KeybindingKeystroke {
/// TODO:
pub inner: Keystroke,
/// TODO:
@@ -65,7 +66,7 @@ impl Keystroke {
///
/// This method assumes that `self` was typed and `target' is in the keymap, and checks
/// both possibilities for self against the target.
- pub fn should_match(&self, target: &Keystroke) -> bool {
+ pub fn should_match(&self, target: &KeybindingKeystroke) -> bool {
#[cfg(not(target_os = "windows"))]
if let Some(key_char) = self
.key_char
@@ -78,7 +79,7 @@ impl Keystroke {
..Default::default()
};
- if &target.key == key_char && target.modifiers == ime_modifiers {
+ if &target.inner.key == key_char && target.inner.modifiers == ime_modifiers {
return true;
}
}
@@ -90,12 +91,12 @@ impl Keystroke {
.filter(|key_char| key_char != &&self.key)
{
// On Windows, if key_char is set, then the typed keystroke produced the key_char
- if &target.key == key_char && target.modifiers == Modifiers::none() {
+ if &target.inner.key == key_char && target.inner.modifiers == Modifiers::none() {
return true;
}
}
- target.modifiers == self.modifiers && target.key == self.key
+ target.inner.modifiers == self.modifiers && target.inner.key == self.key
}
/// key syntax is:
@@ -273,7 +274,7 @@ impl Keystroke {
}
}
-impl KeybindKeystroke {
+impl KeybindingKeystroke {
/// TODO:
pub fn parse(source: &str) -> std::result::Result<Self, InvalidKeystrokeError> {
let keystroke = Keystroke::parse(source)?;
@@ -281,12 +282,22 @@ impl KeybindKeystroke {
mut modifiers, key, ..
} = keystroke.clone();
let (key, modifiers) = temp_keyboard_mapper(key, modifiers);
- Ok(KeybindKeystroke {
+ Ok(KeybindingKeystroke {
inner: keystroke,
modifiers,
key,
})
}
+
+ /// TODO:
+ pub fn to_string(&self) -> String {
+ let keystroke = Keystroke {
+ modifiers: self.modifiers,
+ key: self.key.clone(),
+ key_char: None,
+ };
+ keystroke.to_string()
+ }
}
fn temp_keyboard_mapper(key: String, mut modifiers: Modifiers) -> (String, Modifiers) {
@@ -3316,7 +3316,7 @@ impl Window {
binding
.keystrokes()
.iter()
- .map(ToString::to_string)
+ .map(|ks| ks.to_string())
.collect::<Vec<_>>()
.join(" ")
})
@@ -3,7 +3,8 @@ use collections::{BTreeMap, HashMap, IndexMap};
use fs::Fs;
use gpui::{
Action, ActionBuildError, App, InvalidKeystrokeError, KEYSTROKE_PARSE_EXPECTED_MESSAGE,
- KeyBinding, KeyBindingContextPredicate, KeyBindingMetaIndex, Keystroke, NoAction, SharedString,
+ KeyBinding, KeyBindingContextPredicate, KeyBindingMetaIndex, KeybindingKeystroke, Keystroke,
+ NoAction, SharedString,
};
use schemars::{JsonSchema, json_schema};
use serde::Deserialize;
@@ -787,7 +788,7 @@ pub enum KeybindUpdateOperation<'a> {
pub struct KeybindUpdateTarget<'a> {
pub context: Option<&'a str>,
- pub keystrokes: &'a [Keystroke],
+ pub keystrokes: &'a [KeybindingKeystroke],
pub action_name: &'a str,
pub use_key_equivalents: bool,
pub input: Option<&'a str>,
@@ -810,7 +811,7 @@ impl<'a> KeybindUpdateTarget<'a> {
fn keystrokes_unparsed(&self) -> String {
let mut keystrokes = String::with_capacity(self.keystrokes.len() * 8);
for keystroke in self.keystrokes {
- keystrokes.push_str(&keystroke.unparse());
+ keystrokes.push_str(&keystroke.inner.unparse());
keystrokes.push(' ');
}
keystrokes.pop();
@@ -1,8 +1,8 @@
use crate::PlatformStyle;
use crate::{Icon, IconName, IconSize, h_flex, prelude::*};
use gpui::{
- Action, AnyElement, App, FocusHandle, Global, IntoElement, Keystroke, Modifiers, Window,
- relative,
+ Action, AnyElement, App, FocusHandle, Global, IntoElement, KeybindingKeystroke, Keystroke,
+ Modifiers, Window, relative,
};
use itertools::Itertools;
@@ -13,7 +13,7 @@ pub struct KeyBinding {
/// More than one keystroke produces a chord.
///
/// This should always contain at least one keystroke.
- pub keystrokes: Vec<Keystroke>,
+ pub keystrokes: Vec<KeybindingKeystroke>,
/// The [`PlatformStyle`] to use when displaying this keybinding.
platform_style: PlatformStyle,
@@ -59,7 +59,7 @@ impl KeyBinding {
cx.try_global::<VimStyle>().is_some_and(|g| g.0)
}
- pub fn new(keystrokes: Vec<Keystroke>, cx: &App) -> Self {
+ pub fn new(keystrokes: Vec<KeybindingKeystroke>, cx: &App) -> Self {
Self {
keystrokes,
platform_style: PlatformStyle::platform(),
@@ -99,16 +99,16 @@ impl KeyBinding {
}
fn render_key(
- keystroke: &Keystroke,
+ key: &str,
color: Option<Color>,
platform_style: PlatformStyle,
size: impl Into<Option<AbsoluteLength>>,
) -> AnyElement {
- let key_icon = icon_for_key(keystroke, platform_style);
+ let key_icon = icon_for_key(key, platform_style);
match key_icon {
Some(icon) => KeyIcon::new(icon, color).size(size).into_any_element(),
None => {
- let key = util::capitalize(&keystroke.key);
+ let key = util::capitalize(key);
Key::new(&key, color).size(size).into_any_element()
}
}
@@ -149,7 +149,7 @@ impl RenderOnce for KeyBinding {
}
pub fn render_keystroke(
- keystroke: &Keystroke,
+ keystroke: &KeybindingKeystroke,
color: Option<Color>,
size: impl Into<Option<AbsoluteLength>>,
platform_style: PlatformStyle,
@@ -163,9 +163,17 @@ pub fn render_keystroke(
let size = size.into();
if use_text {
- let element = Key::new(keystroke_text(&keystroke, platform_style, vim_mode), color)
- .size(size)
- .into_any_element();
+ let element = Key::new(
+ keystroke_text(
+ &keystroke.modifiers,
+ &keystroke.key,
+ platform_style,
+ vim_mode,
+ ),
+ color,
+ )
+ .size(size)
+ .into_any_element();
vec![element]
} else {
let mut elements = Vec::new();
@@ -176,13 +184,13 @@ pub fn render_keystroke(
size,
true,
));
- elements.push(render_key(&keystroke, color, platform_style, size));
+ elements.push(render_key(&keystroke.key, color, platform_style, size));
elements
}
}
-fn icon_for_key(keystroke: &Keystroke, platform_style: PlatformStyle) -> Option<IconName> {
- match keystroke.key.as_str() {
+fn icon_for_key(key: &str, platform_style: PlatformStyle) -> Option<IconName> {
+ match key {
"left" => Some(IconName::ArrowLeft),
"right" => Some(IconName::ArrowRight),
"up" => Some(IconName::ArrowUp),
@@ -382,27 +390,44 @@ pub fn text_for_action(action: &dyn Action, window: &Window, cx: &App) -> Option
Some(text_for_keystrokes(key_binding.keystrokes(), cx))
}
-pub fn text_for_keystrokes(keystrokes: &[Keystroke], cx: &App) -> String {
+pub fn text_for_keystrokes(keystrokes: &[KeybindingKeystroke], cx: &App) -> String {
let platform_style = PlatformStyle::platform();
let vim_enabled = cx.try_global::<VimStyle>().is_some();
keystrokes
.iter()
- .map(|keystroke| keystroke_text(keystroke, platform_style, vim_enabled))
+ .map(|keystroke| {
+ keystroke_text(
+ &keystroke.modifiers,
+ &keystroke.key,
+ platform_style,
+ vim_enabled,
+ )
+ })
.join(" ")
}
pub fn text_for_keystroke(keystroke: &Keystroke, cx: &App) -> String {
let platform_style = PlatformStyle::platform();
let vim_enabled = cx.try_global::<VimStyle>().is_some();
- keystroke_text(keystroke, platform_style, vim_enabled)
+ keystroke_text(
+ &keystroke.modifiers,
+ &keystroke.key,
+ platform_style,
+ vim_enabled,
+ )
}
/// Returns a textual representation of the given [`Keystroke`].
-fn keystroke_text(keystroke: &Keystroke, platform_style: PlatformStyle, vim_mode: bool) -> String {
+fn keystroke_text(
+ modifiers: &Modifiers,
+ key: &str,
+ platform_style: PlatformStyle,
+ vim_mode: bool,
+) -> String {
let mut text = String::new();
let delimiter = '-';
- if keystroke.modifiers.function {
+ if modifiers.function {
match vim_mode {
false => text.push_str("Fn"),
true => text.push_str("fn"),
@@ -411,7 +436,7 @@ fn keystroke_text(keystroke: &Keystroke, platform_style: PlatformStyle, vim_mode
text.push(delimiter);
}
- if keystroke.modifiers.control {
+ if modifiers.control {
match (platform_style, vim_mode) {
(PlatformStyle::Mac, false) => text.push_str("Control"),
(PlatformStyle::Linux | PlatformStyle::Windows, false) => text.push_str("Ctrl"),
@@ -421,7 +446,7 @@ fn keystroke_text(keystroke: &Keystroke, platform_style: PlatformStyle, vim_mode
text.push(delimiter);
}
- if keystroke.modifiers.platform {
+ if modifiers.platform {
match (platform_style, vim_mode) {
(PlatformStyle::Mac, false) => text.push_str("Command"),
(PlatformStyle::Mac, true) => text.push_str("cmd"),
@@ -434,7 +459,7 @@ fn keystroke_text(keystroke: &Keystroke, platform_style: PlatformStyle, vim_mode
text.push(delimiter);
}
- if keystroke.modifiers.alt {
+ if modifiers.alt {
match (platform_style, vim_mode) {
(PlatformStyle::Mac, false) => text.push_str("Option"),
(PlatformStyle::Linux | PlatformStyle::Windows, false) => text.push_str("Alt"),
@@ -444,7 +469,7 @@ fn keystroke_text(keystroke: &Keystroke, platform_style: PlatformStyle, vim_mode
text.push(delimiter);
}
- if keystroke.modifiers.shift {
+ if modifiers.shift {
match (platform_style, vim_mode) {
(_, false) => text.push_str("Shift"),
(_, true) => text.push_str("shift"),
@@ -453,9 +478,9 @@ fn keystroke_text(keystroke: &Keystroke, platform_style: PlatformStyle, vim_mode
}
if vim_mode {
- text.push_str(&keystroke.key)
+ text.push_str(key)
} else {
- let key = match keystroke.key.as_str() {
+ let key = match key {
"pageup" => "PageUp",
"pagedown" => "PageDown",
key => &util::capitalize(key),