wip
This commit is contained in:
parent
cb8b7b03cc
commit
fa3abe789c
8 changed files with 190 additions and 324 deletions
|
@ -19,6 +19,10 @@ static KEYMAP_LINUX: LazyLock<KeymapFile> = LazyLock::new(|| {
|
|||
load_keymap("keymaps/default-linux.json").expect("Failed to load Linux keymap")
|
||||
});
|
||||
|
||||
static KEYMAP_WINDOWS: LazyLock<KeymapFile> = LazyLock::new(|| {
|
||||
load_keymap("keymaps/default-windows.json").expect("Failed to load Windows keymap")
|
||||
});
|
||||
|
||||
static ALL_ACTIONS: LazyLock<Vec<ActionDef>> = LazyLock::new(dump_all_gpui_actions);
|
||||
|
||||
const FRONT_MATTER_COMMENT: &str = "<!-- ZED_META {} -->";
|
||||
|
@ -216,6 +220,7 @@ fn find_binding(os: &str, action: &str) -> Option<String> {
|
|||
let keymap = match os {
|
||||
"macos" => &KEYMAP_MACOS,
|
||||
"linux" | "freebsd" => &KEYMAP_LINUX,
|
||||
"windows" => &KEYMAP_WINDOWS,
|
||||
_ => unreachable!("Not a valid OS: {}", os),
|
||||
};
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ use std::rc::Rc;
|
|||
use collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
Action, AsKeystroke, InvalidKeystrokeError, KeyBindingContextPredicate, KeybindingKeystroke,
|
||||
Keystroke, SharedString,
|
||||
Action, AsKeystroke, DummyKeyboardMapper, InvalidKeystrokeError, KeyBindingContextPredicate,
|
||||
KeybindingKeystroke, Keystroke, PlatformKeyboardMapper, SharedString,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
|
@ -35,7 +35,16 @@ impl KeyBinding {
|
|||
pub fn new<A: Action>(keystrokes: &str, action: A, context: Option<&str>) -> Self {
|
||||
let context_predicate =
|
||||
context.map(|context| KeyBindingContextPredicate::parse(context).unwrap().into());
|
||||
Self::load(keystrokes, Box::new(action), context_predicate, None, None).unwrap()
|
||||
Self::load(
|
||||
keystrokes,
|
||||
Box::new(action),
|
||||
context_predicate,
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
&DummyKeyboardMapper,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Load a keybinding from the given raw data.
|
||||
|
@ -43,12 +52,14 @@ impl KeyBinding {
|
|||
keystrokes: &str,
|
||||
action: Box<dyn Action>,
|
||||
context_predicate: Option<Rc<KeyBindingContextPredicate>>,
|
||||
use_key_equivalents: bool,
|
||||
key_equivalents: Option<&HashMap<char, char>>,
|
||||
action_input: Option<SharedString>,
|
||||
keyboard_mapper: &dyn PlatformKeyboardMapper,
|
||||
) -> std::result::Result<Self, InvalidKeystrokeError> {
|
||||
let mut keystrokes: SmallVec<[Keystroke; 2]> = keystrokes
|
||||
.split_whitespace()
|
||||
.map(|source| Keystroke::parse(source).map(|keystroke| keystroke.into_shifted()))
|
||||
.map(Keystroke::parse)
|
||||
.collect::<std::result::Result<_, _>>()?;
|
||||
|
||||
if let Some(equivalents) = key_equivalents {
|
||||
|
@ -63,7 +74,9 @@ impl KeyBinding {
|
|||
|
||||
let keystrokes = keystrokes
|
||||
.into_iter()
|
||||
.map(KeybindingKeystroke::new)
|
||||
.map(|keystroke| {
|
||||
KeybindingKeystroke::new(keystroke, use_key_equivalents, keyboard_mapper)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Self {
|
||||
|
|
|
@ -11,13 +11,22 @@ pub trait PlatformKeyboardLayout {
|
|||
/// A trait for platform-specific keyboard mappings
|
||||
pub trait PlatformKeyboardMapper {
|
||||
/// Map a key equivalent to its platform-specific representation
|
||||
fn map_key_equivalent(&self, keystroke: Keystroke) -> KeybindingKeystroke;
|
||||
fn map_key_equivalent(
|
||||
&self,
|
||||
keystroke: Keystroke,
|
||||
use_key_equivalents: bool,
|
||||
) -> KeybindingKeystroke;
|
||||
}
|
||||
|
||||
pub(crate) struct DummyKeyboardMapper;
|
||||
/// A dummy implementation of the platform keyboard mapper
|
||||
pub struct DummyKeyboardMapper;
|
||||
|
||||
impl PlatformKeyboardMapper for DummyKeyboardMapper {
|
||||
fn map_key_equivalent(&self, keystroke: Keystroke) -> KeybindingKeystroke {
|
||||
KeybindingKeystroke::new(keystroke)
|
||||
fn map_key_equivalent(
|
||||
&self,
|
||||
keystroke: Keystroke,
|
||||
_use_key_equivalents: bool,
|
||||
) -> KeybindingKeystroke {
|
||||
KeybindingKeystroke::from_keystroke(keystroke)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ use std::{
|
|||
fmt::{Display, Write},
|
||||
};
|
||||
|
||||
use crate::PlatformKeyboardMapper;
|
||||
|
||||
/// TODO:
|
||||
pub trait AsKeystroke {
|
||||
/// TODO:
|
||||
|
@ -281,30 +283,23 @@ impl Keystroke {
|
|||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// TODO:
|
||||
pub fn into_shifted(self) -> Self {
|
||||
let Keystroke {
|
||||
modifiers,
|
||||
key,
|
||||
key_char,
|
||||
} = self;
|
||||
let (key, modifiers) = into_shifted_key(key, modifiers);
|
||||
Self {
|
||||
key,
|
||||
modifiers,
|
||||
key_char,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeybindingKeystroke {
|
||||
/// Create a new keybinding keystroke from the given keystroke
|
||||
pub fn new(inner: Keystroke) -> Self {
|
||||
let key = inner.key.clone();
|
||||
let modifiers = inner.modifiers;
|
||||
pub fn new(
|
||||
inner: Keystroke,
|
||||
use_key_equivalents: bool,
|
||||
keyboard_mapper: &dyn PlatformKeyboardMapper,
|
||||
) -> Self {
|
||||
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,
|
||||
inner: keystroke,
|
||||
modifiers,
|
||||
key,
|
||||
}
|
||||
|
@ -608,191 +603,6 @@ impl AsKeystroke for KeybindingKeystroke {
|
|||
}
|
||||
}
|
||||
|
||||
fn to_unshifted_key(key: &str, modifiers: &Modifiers) -> (String, Modifiers) {
|
||||
let mut modifiers = modifiers.clone();
|
||||
match key {
|
||||
"~" => {
|
||||
modifiers.shift = true;
|
||||
("`".to_string(), modifiers)
|
||||
}
|
||||
"!" => {
|
||||
modifiers.shift = true;
|
||||
("1".to_string(), modifiers)
|
||||
}
|
||||
"@" => {
|
||||
modifiers.shift = true;
|
||||
("2".to_string(), modifiers)
|
||||
}
|
||||
"#" => {
|
||||
modifiers.shift = true;
|
||||
("3".to_string(), modifiers)
|
||||
}
|
||||
"$" => {
|
||||
modifiers.shift = true;
|
||||
("4".to_string(), modifiers)
|
||||
}
|
||||
"%" => {
|
||||
modifiers.shift = true;
|
||||
("5".to_string(), modifiers)
|
||||
}
|
||||
"^" => {
|
||||
modifiers.shift = true;
|
||||
("6".to_string(), modifiers)
|
||||
}
|
||||
"&" => {
|
||||
modifiers.shift = true;
|
||||
("7".to_string(), modifiers)
|
||||
}
|
||||
"*" => {
|
||||
modifiers.shift = true;
|
||||
("8".to_string(), modifiers)
|
||||
}
|
||||
"(" => {
|
||||
modifiers.shift = true;
|
||||
("9".to_string(), modifiers)
|
||||
}
|
||||
")" => {
|
||||
modifiers.shift = true;
|
||||
("0".to_string(), modifiers)
|
||||
}
|
||||
"_" => {
|
||||
modifiers.shift = true;
|
||||
("-".to_string(), modifiers)
|
||||
}
|
||||
"+" => {
|
||||
modifiers.shift = true;
|
||||
("=".to_string(), modifiers)
|
||||
}
|
||||
"{" => {
|
||||
modifiers.shift = true;
|
||||
("[".to_string(), modifiers)
|
||||
}
|
||||
"}" => {
|
||||
modifiers.shift = true;
|
||||
("]".to_string(), modifiers)
|
||||
}
|
||||
"|" => {
|
||||
modifiers.shift = true;
|
||||
("\\".to_string(), modifiers)
|
||||
}
|
||||
":" => {
|
||||
modifiers.shift = true;
|
||||
(";".to_string(), modifiers)
|
||||
}
|
||||
"\"" => {
|
||||
modifiers.shift = true;
|
||||
("'".to_string(), modifiers)
|
||||
}
|
||||
"<" => {
|
||||
modifiers.shift = true;
|
||||
(",".to_string(), modifiers)
|
||||
}
|
||||
">" => {
|
||||
modifiers.shift = true;
|
||||
(">".to_string(), modifiers)
|
||||
}
|
||||
"?" => {
|
||||
modifiers.shift = true;
|
||||
("/".to_string(), modifiers)
|
||||
}
|
||||
_ => (key.to_string(), modifiers),
|
||||
}
|
||||
}
|
||||
|
||||
fn into_shifted_key(key: String, mut modifiers: Modifiers) -> (String, Modifiers) {
|
||||
if !modifiers.shift {
|
||||
(key, modifiers)
|
||||
} else {
|
||||
match key.as_str() {
|
||||
"`" => {
|
||||
modifiers.shift = false;
|
||||
("~".to_string(), modifiers)
|
||||
}
|
||||
"1" => {
|
||||
modifiers.shift = false;
|
||||
("!".to_string(), modifiers)
|
||||
}
|
||||
"2" => {
|
||||
modifiers.shift = false;
|
||||
("@".to_string(), modifiers)
|
||||
}
|
||||
"3" => {
|
||||
modifiers.shift = false;
|
||||
("#".to_string(), modifiers)
|
||||
}
|
||||
"4" => {
|
||||
modifiers.shift = false;
|
||||
("$".to_string(), modifiers)
|
||||
}
|
||||
"5" => {
|
||||
modifiers.shift = false;
|
||||
("%".to_string(), modifiers)
|
||||
}
|
||||
"6" => {
|
||||
modifiers.shift = false;
|
||||
("^".to_string(), modifiers)
|
||||
}
|
||||
"7" => {
|
||||
modifiers.shift = false;
|
||||
("&".to_string(), modifiers)
|
||||
}
|
||||
"8" => {
|
||||
modifiers.shift = false;
|
||||
("*".to_string(), modifiers)
|
||||
}
|
||||
"9" => {
|
||||
modifiers.shift = false;
|
||||
("(".to_string(), modifiers)
|
||||
}
|
||||
"0" => {
|
||||
modifiers.shift = false;
|
||||
(")".to_string(), modifiers)
|
||||
}
|
||||
"-" => {
|
||||
modifiers.shift = false;
|
||||
("_".to_string(), modifiers)
|
||||
}
|
||||
"=" => {
|
||||
modifiers.shift = false;
|
||||
("+".to_string(), modifiers)
|
||||
}
|
||||
"[" => {
|
||||
modifiers.shift = false;
|
||||
("{".to_string(), modifiers)
|
||||
}
|
||||
"]" => {
|
||||
modifiers.shift = false;
|
||||
("}".to_string(), modifiers)
|
||||
}
|
||||
"\\" => {
|
||||
modifiers.shift = false;
|
||||
("|".to_string(), modifiers)
|
||||
}
|
||||
";" => {
|
||||
modifiers.shift = false;
|
||||
(":".to_string(), modifiers)
|
||||
}
|
||||
"'" => {
|
||||
modifiers.shift = false;
|
||||
("\"".to_string(), modifiers)
|
||||
}
|
||||
"," => {
|
||||
modifiers.shift = false;
|
||||
("<".to_string(), modifiers)
|
||||
}
|
||||
"." => {
|
||||
modifiers.shift = false;
|
||||
(">".to_string(), modifiers)
|
||||
}
|
||||
"/" => {
|
||||
modifiers.shift = false;
|
||||
("?".to_string(), modifiers)
|
||||
}
|
||||
_ => (key, modifiers),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn display_modifiers(modifiers: &Modifiers, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if modifiers.control {
|
||||
#[cfg(target_os = "macos")]
|
||||
|
@ -858,45 +668,3 @@ fn display_key(key: &str, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|||
};
|
||||
f.write_char(key)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{KeybindingKeystroke, Keystroke, Modifiers};
|
||||
|
||||
#[test]
|
||||
fn test_parsing_keystroke_on_windows() {
|
||||
// On windows, users prefer to use "ctrl-shift-key", so here we support
|
||||
// both "ctrl-$" and "ctrl-shift-4"
|
||||
let source = "ctrl-$";
|
||||
let keystroke = Keystroke::parse(source).unwrap();
|
||||
assert_eq!(keystroke.modifiers, Modifiers::control());
|
||||
assert_eq!(keystroke.key, "$");
|
||||
assert_eq!(keystroke.key_char, None);
|
||||
|
||||
let keystroke_display = KeybindingKeystroke::new(keystroke.clone());
|
||||
assert_eq!(keystroke_display.inner, keystroke);
|
||||
assert_eq!(keystroke_display.key, "4");
|
||||
assert_eq!(keystroke_display.modifiers, Modifiers::control_shift());
|
||||
|
||||
let source = "ctrl-shift-4";
|
||||
let keystroke = Keystroke::parse(source).unwrap();
|
||||
assert_eq!(keystroke.modifiers, Modifiers::control_shift());
|
||||
assert_eq!(keystroke.key, "4");
|
||||
assert_eq!(keystroke.key_char, None);
|
||||
|
||||
let keystroke = keystroke.into_shifted();
|
||||
assert_eq!(keystroke.modifiers, Modifiers::control());
|
||||
assert_eq!(keystroke.key, "$");
|
||||
let keystroke_display = KeybindingKeystroke::new(keystroke.clone());
|
||||
assert_eq!(
|
||||
keystroke_display.inner,
|
||||
Keystroke {
|
||||
modifiers: Modifiers::control(),
|
||||
key: "$".to_string(),
|
||||
key_char: None
|
||||
}
|
||||
);
|
||||
assert_eq!(keystroke_display.key, "4");
|
||||
assert_eq!(keystroke_display.modifiers, Modifiers::control_shift());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use anyhow::Result;
|
||||
use collections::HashMap;
|
||||
use windows::Win32::UI::{
|
||||
Input::KeyboardAndMouse::{
|
||||
GetKeyboardLayoutNameW, MAPVK_VK_TO_CHAR, MAPVK_VK_TO_VSC, MapVirtualKeyW, ToUnicode,
|
||||
|
@ -19,7 +20,11 @@ pub(crate) struct WindowsKeyboardLayout {
|
|||
name: String,
|
||||
}
|
||||
|
||||
pub(crate) struct WindowsKeyboardMapper;
|
||||
pub(crate) struct WindowsKeyboardMapper {
|
||||
key_to_vkey: HashMap<String, (u16, bool)>,
|
||||
vkey_to_key: HashMap<u16, String>,
|
||||
vkey_to_shifted: HashMap<u16, String>,
|
||||
}
|
||||
|
||||
impl PlatformKeyboardLayout for WindowsKeyboardLayout {
|
||||
fn id(&self) -> &str {
|
||||
|
@ -32,9 +37,14 @@ impl PlatformKeyboardLayout for WindowsKeyboardLayout {
|
|||
}
|
||||
|
||||
impl PlatformKeyboardMapper for WindowsKeyboardMapper {
|
||||
fn map_key_equivalent(&self, mut keystroke: Keystroke) -> KeybindingKeystroke {
|
||||
let Some((vkey, shifted_key)) = key_needs_processing(&keystroke.key) else {
|
||||
return KeybindingKeystroke::new(keystroke);
|
||||
fn map_key_equivalent(
|
||||
&self,
|
||||
mut keystroke: Keystroke,
|
||||
use_key_equivalents: bool,
|
||||
) -> KeybindingKeystroke {
|
||||
let Some((vkey, shifted_key)) = self.get_vkey_from_key(&keystroke.key, use_key_equivalents)
|
||||
else {
|
||||
return KeybindingKeystroke::from_keystroke(keystroke);
|
||||
};
|
||||
if shifted_key && keystroke.modifiers.shift {
|
||||
log::warn!(
|
||||
|
@ -46,31 +56,22 @@ impl PlatformKeyboardMapper for WindowsKeyboardMapper {
|
|||
let shift = shifted_key || keystroke.modifiers.shift;
|
||||
keystroke.modifiers.shift = false;
|
||||
|
||||
let Some(key) = get_key_from_vkey(vkey) else {
|
||||
let Some(key) = self.vkey_to_key.get(&vkey).cloned() else {
|
||||
log::error!(
|
||||
"Failed to map key equivalent '{:?}' to a valid key",
|
||||
keystroke
|
||||
);
|
||||
return KeybindingKeystroke::new(keystroke);
|
||||
return KeybindingKeystroke::from_keystroke(keystroke);
|
||||
};
|
||||
|
||||
keystroke.key = if shift {
|
||||
let scan_code = unsafe { MapVirtualKeyW(vkey.0 as u32, MAPVK_VK_TO_VSC) };
|
||||
if scan_code == 0 {
|
||||
log::error!(
|
||||
"Failed to map keystroke {:?} with virtual key '{:?}' to a scan code",
|
||||
keystroke,
|
||||
vkey
|
||||
);
|
||||
return KeybindingKeystroke::new(keystroke);
|
||||
}
|
||||
let Some(shifted_key) = get_shifted_key(vkey, scan_code) else {
|
||||
let Some(shifted_key) = self.vkey_to_shifted.get(&vkey).cloned() else {
|
||||
log::error!(
|
||||
"Failed to map keystroke {:?} with virtual key '{:?}' to a shifted key",
|
||||
keystroke,
|
||||
vkey
|
||||
);
|
||||
return KeybindingKeystroke::new(keystroke);
|
||||
return KeybindingKeystroke::from_keystroke(keystroke);
|
||||
};
|
||||
shifted_key
|
||||
} else {
|
||||
|
@ -113,7 +114,36 @@ impl WindowsKeyboardLayout {
|
|||
|
||||
impl WindowsKeyboardMapper {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self
|
||||
let mut key_to_vkey = HashMap::default();
|
||||
let mut vkey_to_key = HashMap::default();
|
||||
let mut vkey_to_shifted = HashMap::default();
|
||||
for vkey in CANDIDATE_VKEYS {
|
||||
if let Some(key) = get_key_from_vkey(*vkey) {
|
||||
key_to_vkey.insert(key.clone(), (vkey.0, false));
|
||||
vkey_to_key.insert(vkey.0, key);
|
||||
}
|
||||
let scan_code = unsafe { MapVirtualKeyW(vkey.0 as u32, MAPVK_VK_TO_VSC) };
|
||||
if scan_code == 0 {
|
||||
continue;
|
||||
}
|
||||
if let Some(shifted_key) = get_shifted_key(*vkey, scan_code) {
|
||||
key_to_vkey.insert(shifted_key.clone(), (vkey.0, true));
|
||||
vkey_to_shifted.insert(vkey.0, shifted_key);
|
||||
}
|
||||
}
|
||||
Self {
|
||||
key_to_vkey,
|
||||
vkey_to_key,
|
||||
vkey_to_shifted,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_vkey_from_key(&self, key: &str, use_key_equivalents: bool) -> Option<(u16, bool)> {
|
||||
if use_key_equivalents {
|
||||
key_needs_processing(key)
|
||||
} else {
|
||||
self.key_to_vkey.get(key).cloned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,54 +240,81 @@ pub(crate) fn generate_key_char(
|
|||
}
|
||||
}
|
||||
|
||||
fn key_needs_processing(key: &str) -> Option<(VIRTUAL_KEY, bool)> {
|
||||
fn key_needs_processing(key: &str) -> Option<(u16, bool)> {
|
||||
match key {
|
||||
"`" => Some((VK_OEM_3, false)),
|
||||
"~" => Some((VK_OEM_3, true)),
|
||||
"1" => Some((VK_1, false)),
|
||||
"!" => Some((VK_1, true)),
|
||||
"2" => Some((VK_2, false)),
|
||||
"@" => Some((VK_2, true)),
|
||||
"3" => Some((VK_3, false)),
|
||||
"#" => Some((VK_3, true)),
|
||||
"4" => Some((VK_4, false)),
|
||||
"$" => Some((VK_4, true)),
|
||||
"5" => Some((VK_5, false)),
|
||||
"%" => Some((VK_5, true)),
|
||||
"6" => Some((VK_6, false)),
|
||||
"^" => Some((VK_6, true)),
|
||||
"7" => Some((VK_7, false)),
|
||||
"&" => Some((VK_7, true)),
|
||||
"8" => Some((VK_8, false)),
|
||||
"*" => Some((VK_8, true)),
|
||||
"9" => Some((VK_9, false)),
|
||||
"(" => Some((VK_9, true)),
|
||||
"0" => Some((VK_0, false)),
|
||||
")" => Some((VK_0, true)),
|
||||
"-" => Some((VK_OEM_MINUS, false)),
|
||||
"_" => Some((VK_OEM_MINUS, true)),
|
||||
"=" => Some((VK_OEM_PLUS, false)),
|
||||
"+" => Some((VK_OEM_PLUS, true)),
|
||||
"[" => Some((VK_OEM_4, false)),
|
||||
"{" => Some((VK_OEM_4, true)),
|
||||
"]" => Some((VK_OEM_6, false)),
|
||||
"}" => Some((VK_OEM_6, true)),
|
||||
"\\" => Some((VK_OEM_5, false)),
|
||||
"|" => Some((VK_OEM_5, true)),
|
||||
";" => Some((VK_OEM_1, false)),
|
||||
":" => Some((VK_OEM_1, true)),
|
||||
"'" => Some((VK_OEM_7, false)),
|
||||
"\"" => Some((VK_OEM_7, true)),
|
||||
"," => Some((VK_OEM_COMMA, false)),
|
||||
"<" => Some((VK_OEM_COMMA, true)),
|
||||
"." => Some((VK_OEM_PERIOD, false)),
|
||||
">" => Some((VK_OEM_PERIOD, true)),
|
||||
"/" => Some((VK_OEM_2, false)),
|
||||
"?" => Some((VK_OEM_2, true)),
|
||||
"`" => Some((VK_OEM_3.0, false)),
|
||||
"~" => Some((VK_OEM_3.0, true)),
|
||||
"1" => Some((VK_1.0, false)),
|
||||
"!" => Some((VK_1.0, true)),
|
||||
"2" => Some((VK_2.0, false)),
|
||||
"@" => Some((VK_2.0, true)),
|
||||
"3" => Some((VK_3.0, false)),
|
||||
"#" => Some((VK_3.0, true)),
|
||||
"4" => Some((VK_4.0, false)),
|
||||
"$" => Some((VK_4.0, true)),
|
||||
"5" => Some((VK_5.0, false)),
|
||||
"%" => Some((VK_5.0, true)),
|
||||
"6" => Some((VK_6.0, false)),
|
||||
"^" => Some((VK_6.0, true)),
|
||||
"7" => Some((VK_7.0, false)),
|
||||
"&" => Some((VK_7.0, true)),
|
||||
"8" => Some((VK_8.0, false)),
|
||||
"*" => Some((VK_8.0, true)),
|
||||
"9" => Some((VK_9.0, false)),
|
||||
"(" => Some((VK_9.0, true)),
|
||||
"0" => Some((VK_0.0, false)),
|
||||
")" => Some((VK_0.0, true)),
|
||||
"-" => Some((VK_OEM_MINUS.0, false)),
|
||||
"_" => Some((VK_OEM_MINUS.0, true)),
|
||||
"=" => Some((VK_OEM_PLUS.0, false)),
|
||||
"+" => Some((VK_OEM_PLUS.0, true)),
|
||||
"[" => Some((VK_OEM_4.0, false)),
|
||||
"{" => Some((VK_OEM_4.0, true)),
|
||||
"]" => Some((VK_OEM_6.0, false)),
|
||||
"}" => Some((VK_OEM_6.0, true)),
|
||||
"\\" => Some((VK_OEM_5.0, false)),
|
||||
"|" => Some((VK_OEM_5.0, true)),
|
||||
";" => Some((VK_OEM_1.0, false)),
|
||||
":" => Some((VK_OEM_1.0, true)),
|
||||
"'" => Some((VK_OEM_7.0, false)),
|
||||
"\"" => Some((VK_OEM_7.0, true)),
|
||||
"," => Some((VK_OEM_COMMA.0, false)),
|
||||
"<" => Some((VK_OEM_COMMA.0, true)),
|
||||
"." => Some((VK_OEM_PERIOD.0, false)),
|
||||
">" => Some((VK_OEM_PERIOD.0, true)),
|
||||
"/" => Some((VK_OEM_2.0, false)),
|
||||
"?" => Some((VK_OEM_2.0, true)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
const CANDIDATE_VKEYS: &[VIRTUAL_KEY] = &[
|
||||
VK_OEM_3,
|
||||
VK_OEM_MINUS,
|
||||
VK_OEM_PLUS,
|
||||
VK_OEM_4,
|
||||
VK_OEM_5,
|
||||
VK_OEM_6,
|
||||
VK_OEM_1,
|
||||
VK_OEM_7,
|
||||
VK_OEM_COMMA,
|
||||
VK_OEM_PERIOD,
|
||||
VK_OEM_2,
|
||||
VK_OEM_102,
|
||||
VK_OEM_8,
|
||||
VK_ABNT_C1,
|
||||
VK_0,
|
||||
VK_1,
|
||||
VK_2,
|
||||
VK_3,
|
||||
VK_4,
|
||||
VK_5,
|
||||
VK_6,
|
||||
VK_7,
|
||||
VK_8,
|
||||
VK_9,
|
||||
];
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{Keystroke, Modifiers, PlatformKeyboardMapper, WindowsKeyboardMapper};
|
||||
|
@ -272,7 +329,7 @@ mod tests {
|
|||
key: "a".to_string(),
|
||||
key_char: None,
|
||||
};
|
||||
let mapped = mapper.map_key_equivalent(keystroke.clone());
|
||||
let mapped = mapper.map_key_equivalent(keystroke.clone(), true);
|
||||
assert_eq!(mapped.inner, keystroke);
|
||||
assert_eq!(mapped.key, "a");
|
||||
assert_eq!(mapped.modifiers, Modifiers::control());
|
||||
|
@ -283,7 +340,7 @@ mod tests {
|
|||
key: "$".to_string(),
|
||||
key_char: None,
|
||||
};
|
||||
let mapped = mapper.map_key_equivalent(keystroke.clone());
|
||||
let mapped = mapper.map_key_equivalent(keystroke.clone(), true);
|
||||
assert_eq!(mapped.inner, keystroke);
|
||||
assert_eq!(mapped.key, "4");
|
||||
assert_eq!(mapped.modifiers, Modifiers::control_shift());
|
||||
|
@ -294,7 +351,7 @@ mod tests {
|
|||
key: "$".to_string(),
|
||||
key_char: None,
|
||||
};
|
||||
let mapped = mapper.map_key_equivalent(keystroke.clone());
|
||||
let mapped = mapper.map_key_equivalent(keystroke.clone(), true);
|
||||
assert_eq!(mapped.inner.modifiers, Modifiers::control());
|
||||
assert_eq!(mapped.key, "4");
|
||||
assert_eq!(mapped.modifiers, Modifiers::control_shift());
|
||||
|
@ -305,7 +362,7 @@ mod tests {
|
|||
key: "4".to_string(),
|
||||
key_char: None,
|
||||
};
|
||||
let mapped = mapper.map_key_equivalent(keystroke.clone());
|
||||
let mapped = mapper.map_key_equivalent(keystroke.clone(), true);
|
||||
assert_eq!(mapped.inner.modifiers, Modifiers::control());
|
||||
assert_eq!(mapped.inner.key, "$");
|
||||
assert_eq!(mapped.key, "4");
|
||||
|
|
|
@ -279,6 +279,7 @@ impl KeymapFile {
|
|||
keystrokes,
|
||||
action,
|
||||
context_predicate.clone(),
|
||||
*use_key_equivalents,
|
||||
key_equivalents,
|
||||
cx,
|
||||
);
|
||||
|
@ -337,6 +338,7 @@ impl KeymapFile {
|
|||
keystrokes: &str,
|
||||
action: &KeymapAction,
|
||||
context: Option<Rc<KeyBindingContextPredicate>>,
|
||||
use_key_equivalents: bool,
|
||||
key_equivalents: Option<&HashMap<char, char>>,
|
||||
cx: &App,
|
||||
) -> std::result::Result<KeyBinding, String> {
|
||||
|
@ -405,8 +407,10 @@ impl KeymapFile {
|
|||
keystrokes,
|
||||
action,
|
||||
context,
|
||||
use_key_equivalents,
|
||||
key_equivalents,
|
||||
action_input_string.map(SharedString::from),
|
||||
cx.keyboard_mapper(),
|
||||
) {
|
||||
Ok(key_binding) => key_binding,
|
||||
Err(InvalidKeystrokeError { keystroke }) => {
|
||||
|
@ -1021,7 +1025,7 @@ impl From<KeybindSource> for KeyBindingMetaIndex {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use gpui::{KeybindingKeystroke, Keystroke};
|
||||
use gpui::{DummyKeyboardMapper, KeybindingKeystroke, Keystroke};
|
||||
use unindent::Unindent;
|
||||
|
||||
use crate::{
|
||||
|
@ -1059,7 +1063,13 @@ mod tests {
|
|||
fn parse_keystrokes(keystrokes: &str) -> Vec<KeybindingKeystroke> {
|
||||
keystrokes
|
||||
.split(' ')
|
||||
.map(|s| KeybindingKeystroke::new(Keystroke::parse(s).expect("Keystrokes valid")))
|
||||
.map(|s| {
|
||||
KeybindingKeystroke::new(
|
||||
Keystroke::parse(s).expect("Keystrokes valid"),
|
||||
false,
|
||||
&DummyKeyboardMapper,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
|
|
@ -89,9 +89,12 @@ pub fn default_settings() -> Cow<'static, str> {
|
|||
#[cfg(target_os = "macos")]
|
||||
pub const DEFAULT_KEYMAP_PATH: &str = "keymaps/default-macos.json";
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[cfg(target_os = "linux")]
|
||||
pub const DEFAULT_KEYMAP_PATH: &str = "keymaps/default-linux.json";
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub const DEFAULT_KEYMAP_PATH: &str = "keymaps/default-windows.json";
|
||||
|
||||
pub fn default_keymap() -> Cow<'static, str> {
|
||||
asset_str::<SettingsAssets>(DEFAULT_KEYMAP_PATH)
|
||||
}
|
||||
|
|
|
@ -301,7 +301,8 @@ impl KeystrokeInput {
|
|||
return;
|
||||
}
|
||||
|
||||
let mut keystroke = KeybindingKeystroke::new(keystroke.clone());
|
||||
let mut keystroke =
|
||||
KeybindingKeystroke::new(keystroke.clone(), false, cx.keyboard_mapper());
|
||||
if let Some(last) = self.keystrokes.last()
|
||||
&& last.key.is_empty()
|
||||
&& (!self.search || self.previous_modifiers.modified())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue