776 lines · 22.5 KB · Rust
RawDownload
use schemars::JsonSchema;use serde::{Deserialize, Serialize};use std::{    error::Error,    fmt::{Display, Write},}; use crate::PlatformKeyboardMapper; /// This is a helper trait so that we can simplify the implementation of some functionspub trait AsKeystroke {    /// Returns the GPUI representation of the keystroke.    fn as_keystroke(&self) -> &Keystroke;} /// A keystroke and associated metadata generated by the platform#[derive(Clone, Debug, Eq, PartialEq, Default, Deserialize, Hash)]pub struct Keystroke {    /// the state of the modifier keys at the time the keystroke was generated    pub modifiers: Modifiers,     /// key is the character printed on the key that was pressed    /// e.g. for option-s, key is "s"    /// On layouts that do not have ascii keys (e.g. Thai)    /// this will be the ASCII-equivalent character (q instead of ๆ),    /// and the typed character will be present in key_char.    pub key: String,     /// key_char is the character that could have been typed when    /// this binding was pressed.    /// e.g. for s this is "s", for option-s "ß", and cmd-s None    pub key_char: Option<String>,} /// Represents a keystroke that can be used in keybindings and displayed to the user.#[derive(Debug, Clone, Eq, PartialEq, Hash)]pub struct KeybindingKeystroke {    /// The GPUI representation of the keystroke.    inner: Keystroke,    /// The modifiers to display.    #[cfg(target_os = "windows")]    display_modifiers: Modifiers,    /// The key to display.    #[cfg(target_os = "windows")]    display_key: String,} /// Error type for `Keystroke::parse`. This is used instead of `anyhow::Error` so that Zed can use/// markdown to display it.#[derive(Debug)]pub struct InvalidKeystrokeError {    /// The invalid keystroke.    pub keystroke: String,} impl Error for InvalidKeystrokeError {} impl Display for InvalidKeystrokeError {    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {        write!(            f,            "Invalid keystroke \"{}\". {}",            self.keystroke, KEYSTROKE_PARSE_EXPECTED_MESSAGE        )    }} /// Sentence explaining what keystroke parser expects, starting with "Expected ..."pub const KEYSTROKE_PARSE_EXPECTED_MESSAGE: &str = "Expected a sequence of modifiers \    (`ctrl`, `alt`, `shift`, `fn`, `cmd`, `super`, or `win`) \    followed by a key, separated by `-`."; impl Keystroke {    /// When matching a key we cannot know whether the user intended to type    /// the key_char or the key itself. On some non-US keyboards keys we use in our    /// bindings are behind option (for example `$` is typed `alt-ç` on a Czech keyboard),    /// and on some keyboards the IME handler converts a sequence of keys into a    /// specific character (for example `"` is typed as `" space` on a brazilian keyboard).    ///    /// 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: &KeybindingKeystroke) -> bool {        #[cfg(not(target_os = "windows"))]        if let Some(key_char) = self            .key_char            .as_ref()            .filter(|key_char| key_char != &&self.key)        {            let ime_modifiers = Modifiers {                control: self.modifiers.control,                platform: self.modifiers.platform,                ..Default::default()            };             if &target.inner.key == key_char && target.inner.modifiers == ime_modifiers {                return true;            }        }         #[cfg(target_os = "windows")]        if let Some(key_char) = self            .key_char            .as_ref()            .filter(|key_char| key_char != &&self.key)        {            // On Windows, if key_char is set, then the typed keystroke produced the key_char            if &target.inner.key == key_char && target.inner.modifiers == Modifiers::none() {                return true;            }        }         target.inner.modifiers == self.modifiers && target.inner.key == self.key    }     /// key syntax is:    /// [secondary-][ctrl-][alt-][shift-][cmd-][fn-]key[->key_char]    /// key_char syntax is only used for generating test events,    /// secondary means "cmd" on macOS and "ctrl" on other platforms    /// when matching a key with an key_char set will be matched without it.    pub fn parse(source: &str) -> std::result::Result<Self, InvalidKeystrokeError> {        let mut modifiers = Modifiers::none();        let mut key = None;        let mut key_char = None;         let mut components = source.split('-').peekable();        while let Some(component) = components.next() {            if component.eq_ignore_ascii_case("ctrl") {                modifiers.control = true;                continue;            }            if component.eq_ignore_ascii_case("alt") {                modifiers.alt = true;                continue;            }            if component.eq_ignore_ascii_case("shift") {                modifiers.shift = true;                continue;            }            if component.eq_ignore_ascii_case("fn") {                modifiers.function = true;                continue;            }            if component.eq_ignore_ascii_case("secondary") {                if cfg!(target_os = "macos") {                    modifiers.platform = true;                } else {                    modifiers.control = true;                };                continue;            }             let is_platform = component.eq_ignore_ascii_case("cmd")                || component.eq_ignore_ascii_case("super")                || component.eq_ignore_ascii_case("win");             if is_platform {                modifiers.platform = true;                continue;            }             let mut key_str = component.to_string();             if let Some(next) = components.peek() {                if next.is_empty() && source.ends_with('-') {                    key = Some(String::from("-"));                    break;                } else if next.len() > 1 && next.starts_with('>') {                    key = Some(key_str);                    key_char = Some(String::from(&next[1..]));                    components.next();                } else {                    return Err(InvalidKeystrokeError {                        keystroke: source.to_owned(),                    });                }                continue;            }             if component.len() == 1 && component.as_bytes()[0].is_ascii_uppercase() {                // Convert to shift + lowercase char                modifiers.shift = true;                key_str.make_ascii_lowercase();            } else {                // convert ascii chars to lowercase so that named keys like "tab" and "enter"                // are accepted case insensitively and stored how we expect so they are matched properly                key_str.make_ascii_lowercase()            }            key = Some(key_str);        }         // Allow for the user to specify a keystroke modifier as the key itself        // This sets the `key` to the modifier, and disables the modifier        key = key.or_else(|| {            use std::mem;            // std::mem::take clears bool incase its true            if mem::take(&mut modifiers.shift) {                Some("shift".to_string())            } else if mem::take(&mut modifiers.control) {                Some("control".to_string())            } else if mem::take(&mut modifiers.alt) {                Some("alt".to_string())            } else if mem::take(&mut modifiers.platform) {                Some("platform".to_string())            } else if mem::take(&mut modifiers.function) {                Some("function".to_string())            } else {                None            }        });         let key = key.ok_or_else(|| InvalidKeystrokeError {            keystroke: source.to_owned(),        })?;         Ok(Keystroke {            modifiers,            key,            key_char,        })    }     /// Produces a representation of this key that Parse can understand.    pub fn unparse(&self) -> String {        unparse(&self.modifiers, &self.key)    }     /// Returns true if this keystroke left    /// the ime system in an incomplete state.    pub fn is_ime_in_progress(&self) -> bool {        self.key_char.is_none()            && (is_printable_key(&self.key) || self.key.is_empty())            && !(self.modifiers.platform                || self.modifiers.control                || self.modifiers.function                || self.modifiers.alt)    }     /// Returns a new keystroke with the key_char filled.    /// This is used for dispatch_keystroke where we want users to    /// be able to simulate typing "space", etc.    pub fn with_simulated_ime(mut self) -> Self {        if self.key_char.is_none()            && !self.modifiers.platform            && !self.modifiers.control            && !self.modifiers.function            && !self.modifiers.alt        {            self.key_char = match self.key.as_str() {                "space" => Some(" ".into()),                "tab" => Some("\t".into()),                "enter" => Some("\n".into()),                key if !is_printable_key(key) || key.is_empty() => None,                key => {                    if self.modifiers.shift {                        Some(key.to_uppercase())                    } else {                        Some(key.into())                    }                }            }        }        self    }} impl KeybindingKeystroke {    #[cfg(target_os = "windows")]    #[expect(missing_docs)]    pub 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,    ) -> Self {        keyboard_mapper.map_key_equivalent(inner, use_key_equivalents)    }     /// 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 {        #[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;    }} fn is_printable_key(key: &str) -> bool {    !matches!(        key,        "f1" | "f2"            | "f3"            | "f4"            | "f5"            | "f6"            | "f7"            | "f8"            | "f9"            | "f10"            | "f11"            | "f12"            | "f13"            | "f14"            | "f15"            | "f16"            | "f17"            | "f18"            | "f19"            | "f20"            | "f21"            | "f22"            | "f23"            | "f24"            | "f25"            | "f26"            | "f27"            | "f28"            | "f29"            | "f30"            | "f31"            | "f32"            | "f33"            | "f34"            | "f35"            | "backspace"            | "delete"            | "left"            | "right"            | "up"            | "down"            | "pageup"            | "pagedown"            | "insert"            | "home"            | "end"            | "back"            | "forward"            | "escape"    )} impl std::fmt::Display for Keystroke {    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {        display_modifiers(&self.modifiers, f)?;        display_key(&self.key, f)    }} impl std::fmt::Display for KeybindingKeystroke {    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {        display_modifiers(self.modifiers(), f)?;        display_key(self.key(), f)    }} /// The state of the modifier keys at some point in time#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize, Hash, JsonSchema)]pub struct Modifiers {    /// The control key    #[serde(default)]    pub control: bool,     /// The alt key    /// Sometimes also known as the 'meta' key    #[serde(default)]    pub alt: bool,     /// The shift key    #[serde(default)]    pub shift: bool,     /// The command key, on macos    /// the windows key, on windows    /// the super key, on linux    #[serde(default)]    pub platform: bool,     /// The function key    #[serde(default)]    pub function: bool,} impl Modifiers {    /// Returns whether any modifier key is pressed.    pub fn modified(&self) -> bool {        self.control || self.alt || self.shift || self.platform || self.function    }     /// Whether the semantically 'secondary' modifier key is pressed.    ///    /// On macOS, this is the command key.    /// On Linux and Windows, this is the control key.    pub fn secondary(&self) -> bool {        #[cfg(target_os = "macos")]        {            self.platform        }         #[cfg(not(target_os = "macos"))]        {            self.control        }    }     /// Returns how many modifier keys are pressed.    pub fn number_of_modifiers(&self) -> u8 {        self.control as u8            + self.alt as u8            + self.shift as u8            + self.platform as u8

Lines 1–500 of 776.