Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-view/src/input.rs')
| -rw-r--r-- | helix-view/src/input.rs | 826 |
1 files changed, 35 insertions, 791 deletions
diff --git a/helix-view/src/input.rs b/helix-view/src/input.rs index 539680a6..1e0ddfe2 100644 --- a/helix-view/src/input.rs +++ b/helix-view/src/input.rs @@ -1,71 +1,17 @@ -//! Input event handling, currently backed by termina. +//! Input event handling, currently backed by crossterm. use anyhow::{anyhow, Error}; -use helix_core::unicode::{segmentation::UnicodeSegmentation, width::UnicodeWidthStr}; +use helix_core::unicode::width::UnicodeWidthStr; use serde::de::{self, Deserialize, Deserializer}; use std::fmt; -pub use crate::keyboard::{KeyCode, KeyModifiers, MediaKeyCode, ModifierKeyCode}; +use crate::keyboard::{KeyCode, KeyModifiers}; -#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Hash)] -pub enum Event { - FocusGained, - FocusLost, - Key(KeyEvent), - Mouse(MouseEvent), - Paste(String), - Resize(u16, u16), - IdleTimeout, -} - -#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] -pub struct MouseEvent { - /// The kind of mouse event that was caused. - pub kind: MouseEventKind, - /// The column that the event occurred on. - pub column: u16, - /// The row that the event occurred on. - pub row: u16, - /// The key modifiers active when the event occurred. - pub modifiers: KeyModifiers, -} - -#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] -pub enum MouseEventKind { - /// Pressed mouse button. Contains the button that was pressed. - Down(MouseButton), - /// Released mouse button. Contains the button that was released. - Up(MouseButton), - /// Moved the mouse cursor while pressing the contained mouse button. - Drag(MouseButton), - /// Moved the mouse cursor while not pressing a mouse button. - Moved, - /// Scrolled mouse wheel downwards (towards the user). - ScrollDown, - /// Scrolled mouse wheel upwards (away from the user). - ScrollUp, - /// Scrolled mouse wheel leftwards. - ScrollLeft, - /// Scrolled mouse wheel rightwards. - ScrollRight, -} - -/// Represents a mouse button. -#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] -pub enum MouseButton { - /// Left mouse button. - Left, - /// Right mouse button. - Right, - /// Middle mouse button. - Middle, -} /// Represents a key event. // We use a newtype here because we want to customize Deserialize and Display. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Clone, Copy, Hash)] pub struct KeyEvent { pub code: KeyCode, pub modifiers: KeyModifiers, - // TODO: termina now supports kind & state if terminal supports kitty's extended protocol } impl KeyEvent { @@ -76,31 +22,6 @@ impl KeyEvent { _ => None, } } - - /// Format the key in such a way that a concatenated sequence - /// of keys can be read easily. - /// - /// ``` - /// # use std::str::FromStr; - /// # use helix_view::input::KeyEvent; - /// - /// let k = KeyEvent::from_str("w").unwrap().key_sequence_format(); - /// assert_eq!(k, "w"); - /// - /// let k = KeyEvent::from_str("C-w").unwrap().key_sequence_format(); - /// assert_eq!(k, "<C-w>"); - /// - /// let k = KeyEvent::from_str(" ").unwrap().key_sequence_format(); - /// assert_eq!(k, "<space>"); - /// ``` - pub fn key_sequence_format(&self) -> String { - let s = self.to_string(); - if s.graphemes(true).count() > 1 { - format!("<{}>", s) - } else { - s - } - } } pub(crate) mod keys { @@ -115,59 +36,24 @@ pub(crate) mod keys { pub(crate) const PAGEUP: &str = "pageup"; pub(crate) const PAGEDOWN: &str = "pagedown"; pub(crate) const TAB: &str = "tab"; + pub(crate) const BACKTAB: &str = "backtab"; pub(crate) const DELETE: &str = "del"; pub(crate) const INSERT: &str = "ins"; pub(crate) const NULL: &str = "null"; pub(crate) const ESC: &str = "esc"; pub(crate) const SPACE: &str = "space"; - pub(crate) const MINUS: &str = "minus"; pub(crate) const LESS_THAN: &str = "lt"; pub(crate) const GREATER_THAN: &str = "gt"; - pub(crate) const CAPS_LOCK: &str = "capslock"; - pub(crate) const SCROLL_LOCK: &str = "scrolllock"; - pub(crate) const NUM_LOCK: &str = "numlock"; - pub(crate) const PRINT_SCREEN: &str = "printscreen"; - pub(crate) const PAUSE: &str = "pause"; - pub(crate) const MENU: &str = "menu"; - pub(crate) const KEYPAD_BEGIN: &str = "keypadbegin"; - pub(crate) const PLAY: &str = "play"; - pub(crate) const PAUSE_MEDIA: &str = "pausemedia"; - pub(crate) const PLAY_PAUSE: &str = "playpause"; - pub(crate) const REVERSE: &str = "reverse"; - pub(crate) const STOP: &str = "stop"; - pub(crate) const FAST_FORWARD: &str = "fastforward"; - pub(crate) const REWIND: &str = "rewind"; - pub(crate) const TRACK_NEXT: &str = "tracknext"; - pub(crate) const TRACK_PREVIOUS: &str = "trackprevious"; - pub(crate) const RECORD: &str = "record"; - pub(crate) const LOWER_VOLUME: &str = "lowervolume"; - pub(crate) const RAISE_VOLUME: &str = "raisevolume"; - pub(crate) const MUTE_VOLUME: &str = "mutevolume"; - pub(crate) const LEFT_SHIFT: &str = "leftshift"; - pub(crate) const LEFT_CONTROL: &str = "leftcontrol"; - pub(crate) const LEFT_ALT: &str = "leftalt"; - pub(crate) const LEFT_SUPER: &str = "leftsuper"; - pub(crate) const LEFT_HYPER: &str = "lefthyper"; - pub(crate) const LEFT_META: &str = "leftmeta"; - pub(crate) const RIGHT_SHIFT: &str = "rightshift"; - pub(crate) const RIGHT_CONTROL: &str = "rightcontrol"; - pub(crate) const RIGHT_ALT: &str = "rightalt"; - pub(crate) const RIGHT_SUPER: &str = "rightsuper"; - pub(crate) const RIGHT_HYPER: &str = "righthyper"; - pub(crate) const RIGHT_META: &str = "rightmeta"; - pub(crate) const ISO_LEVEL_3_SHIFT: &str = "isolevel3shift"; - pub(crate) const ISO_LEVEL_5_SHIFT: &str = "isolevel5shift"; + pub(crate) const PLUS: &str = "plus"; + pub(crate) const MINUS: &str = "minus"; + pub(crate) const SEMICOLON: &str = "semicolon"; + pub(crate) const PERCENT: &str = "percent"; } impl fmt::Display for KeyEvent { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!( - "{}{}{}{}", - if self.modifiers.contains(KeyModifiers::SUPER) { - "Meta-" - } else { - "" - }, + "{}{}{}", if self.modifiers.contains(KeyModifiers::SHIFT) { "S-" } else { @@ -196,54 +82,20 @@ impl fmt::Display for KeyEvent { KeyCode::PageUp => f.write_str(keys::PAGEUP)?, KeyCode::PageDown => f.write_str(keys::PAGEDOWN)?, KeyCode::Tab => f.write_str(keys::TAB)?, + KeyCode::BackTab => f.write_str(keys::BACKTAB)?, KeyCode::Delete => f.write_str(keys::DELETE)?, KeyCode::Insert => f.write_str(keys::INSERT)?, KeyCode::Null => f.write_str(keys::NULL)?, KeyCode::Esc => f.write_str(keys::ESC)?, KeyCode::Char(' ') => f.write_str(keys::SPACE)?, - KeyCode::Char('-') => f.write_str(keys::MINUS)?, KeyCode::Char('<') => f.write_str(keys::LESS_THAN)?, KeyCode::Char('>') => f.write_str(keys::GREATER_THAN)?, + KeyCode::Char('+') => f.write_str(keys::PLUS)?, + KeyCode::Char('-') => f.write_str(keys::MINUS)?, + KeyCode::Char(';') => f.write_str(keys::SEMICOLON)?, + KeyCode::Char('%') => f.write_str(keys::PERCENT)?, KeyCode::F(i) => f.write_fmt(format_args!("F{}", i))?, KeyCode::Char(c) => f.write_fmt(format_args!("{}", c))?, - KeyCode::CapsLock => f.write_str(keys::CAPS_LOCK)?, - KeyCode::ScrollLock => f.write_str(keys::SCROLL_LOCK)?, - KeyCode::NumLock => f.write_str(keys::NUM_LOCK)?, - KeyCode::PrintScreen => f.write_str(keys::PRINT_SCREEN)?, - KeyCode::Pause => f.write_str(keys::PAUSE)?, - KeyCode::Menu => f.write_str(keys::MENU)?, - KeyCode::KeypadBegin => f.write_str(keys::KEYPAD_BEGIN)?, - KeyCode::Media(MediaKeyCode::Play) => f.write_str(keys::PLAY)?, - KeyCode::Media(MediaKeyCode::Pause) => f.write_str(keys::PAUSE_MEDIA)?, - KeyCode::Media(MediaKeyCode::PlayPause) => f.write_str(keys::PLAY_PAUSE)?, - KeyCode::Media(MediaKeyCode::Stop) => f.write_str(keys::STOP)?, - KeyCode::Media(MediaKeyCode::Reverse) => f.write_str(keys::REVERSE)?, - KeyCode::Media(MediaKeyCode::FastForward) => f.write_str(keys::FAST_FORWARD)?, - KeyCode::Media(MediaKeyCode::Rewind) => f.write_str(keys::REWIND)?, - KeyCode::Media(MediaKeyCode::TrackNext) => f.write_str(keys::TRACK_NEXT)?, - KeyCode::Media(MediaKeyCode::TrackPrevious) => f.write_str(keys::TRACK_PREVIOUS)?, - KeyCode::Media(MediaKeyCode::Record) => f.write_str(keys::RECORD)?, - KeyCode::Media(MediaKeyCode::LowerVolume) => f.write_str(keys::LOWER_VOLUME)?, - KeyCode::Media(MediaKeyCode::RaiseVolume) => f.write_str(keys::RAISE_VOLUME)?, - KeyCode::Media(MediaKeyCode::MuteVolume) => f.write_str(keys::MUTE_VOLUME)?, - KeyCode::Modifier(ModifierKeyCode::LeftShift) => f.write_str(keys::LEFT_SHIFT)?, - KeyCode::Modifier(ModifierKeyCode::LeftControl) => f.write_str(keys::LEFT_CONTROL)?, - KeyCode::Modifier(ModifierKeyCode::LeftAlt) => f.write_str(keys::LEFT_ALT)?, - KeyCode::Modifier(ModifierKeyCode::LeftSuper) => f.write_str(keys::LEFT_SUPER)?, - KeyCode::Modifier(ModifierKeyCode::LeftHyper) => f.write_str(keys::LEFT_HYPER)?, - KeyCode::Modifier(ModifierKeyCode::LeftMeta) => f.write_str(keys::LEFT_META)?, - KeyCode::Modifier(ModifierKeyCode::RightShift) => f.write_str(keys::RIGHT_SHIFT)?, - KeyCode::Modifier(ModifierKeyCode::RightControl) => f.write_str(keys::RIGHT_CONTROL)?, - KeyCode::Modifier(ModifierKeyCode::RightAlt) => f.write_str(keys::RIGHT_ALT)?, - KeyCode::Modifier(ModifierKeyCode::RightSuper) => f.write_str(keys::RIGHT_SUPER)?, - KeyCode::Modifier(ModifierKeyCode::RightHyper) => f.write_str(keys::RIGHT_HYPER)?, - KeyCode::Modifier(ModifierKeyCode::RightMeta) => f.write_str(keys::RIGHT_META)?, - KeyCode::Modifier(ModifierKeyCode::IsoLevel3Shift) => { - f.write_str(keys::ISO_LEVEL_3_SHIFT)? - } - KeyCode::Modifier(ModifierKeyCode::IsoLevel5Shift) => { - f.write_str(keys::ISO_LEVEL_5_SHIFT)? - } }; Ok(()) } @@ -264,49 +116,21 @@ impl UnicodeWidthStr for KeyEvent { KeyCode::PageUp => keys::PAGEUP.len(), KeyCode::PageDown => keys::PAGEDOWN.len(), KeyCode::Tab => keys::TAB.len(), + KeyCode::BackTab => keys::BACKTAB.len(), KeyCode::Delete => keys::DELETE.len(), KeyCode::Insert => keys::INSERT.len(), KeyCode::Null => keys::NULL.len(), KeyCode::Esc => keys::ESC.len(), KeyCode::Char(' ') => keys::SPACE.len(), + KeyCode::Char('<') => keys::LESS_THAN.len(), + KeyCode::Char('>') => keys::GREATER_THAN.len(), + KeyCode::Char('+') => keys::PLUS.len(), KeyCode::Char('-') => keys::MINUS.len(), + KeyCode::Char(';') => keys::SEMICOLON.len(), + KeyCode::Char('%') => keys::PERCENT.len(), KeyCode::F(1..=9) => 2, KeyCode::F(_) => 3, KeyCode::Char(c) => c.width().unwrap_or(0), - KeyCode::CapsLock => keys::CAPS_LOCK.len(), - KeyCode::ScrollLock => keys::SCROLL_LOCK.len(), - KeyCode::NumLock => keys::NUM_LOCK.len(), - KeyCode::PrintScreen => keys::PRINT_SCREEN.len(), - KeyCode::Pause => keys::PAUSE.len(), - KeyCode::Menu => keys::MENU.len(), - KeyCode::KeypadBegin => keys::KEYPAD_BEGIN.len(), - KeyCode::Media(MediaKeyCode::Play) => keys::PLAY.len(), - KeyCode::Media(MediaKeyCode::Pause) => keys::PAUSE_MEDIA.len(), - KeyCode::Media(MediaKeyCode::PlayPause) => keys::PLAY_PAUSE.len(), - KeyCode::Media(MediaKeyCode::Stop) => keys::STOP.len(), - KeyCode::Media(MediaKeyCode::Reverse) => keys::REVERSE.len(), - KeyCode::Media(MediaKeyCode::FastForward) => keys::FAST_FORWARD.len(), - KeyCode::Media(MediaKeyCode::Rewind) => keys::REWIND.len(), - KeyCode::Media(MediaKeyCode::TrackNext) => keys::TRACK_NEXT.len(), - KeyCode::Media(MediaKeyCode::TrackPrevious) => keys::TRACK_PREVIOUS.len(), - KeyCode::Media(MediaKeyCode::Record) => keys::RECORD.len(), - KeyCode::Media(MediaKeyCode::LowerVolume) => keys::LOWER_VOLUME.len(), - KeyCode::Media(MediaKeyCode::RaiseVolume) => keys::RAISE_VOLUME.len(), - KeyCode::Media(MediaKeyCode::MuteVolume) => keys::MUTE_VOLUME.len(), - KeyCode::Modifier(ModifierKeyCode::LeftShift) => keys::LEFT_SHIFT.len(), - KeyCode::Modifier(ModifierKeyCode::LeftControl) => keys::LEFT_CONTROL.len(), - KeyCode::Modifier(ModifierKeyCode::LeftAlt) => keys::LEFT_ALT.len(), - KeyCode::Modifier(ModifierKeyCode::LeftSuper) => keys::LEFT_SUPER.len(), - KeyCode::Modifier(ModifierKeyCode::LeftHyper) => keys::LEFT_HYPER.len(), - KeyCode::Modifier(ModifierKeyCode::LeftMeta) => keys::LEFT_META.len(), - KeyCode::Modifier(ModifierKeyCode::RightShift) => keys::RIGHT_SHIFT.len(), - KeyCode::Modifier(ModifierKeyCode::RightControl) => keys::RIGHT_CONTROL.len(), - KeyCode::Modifier(ModifierKeyCode::RightAlt) => keys::RIGHT_ALT.len(), - KeyCode::Modifier(ModifierKeyCode::RightSuper) => keys::RIGHT_SUPER.len(), - KeyCode::Modifier(ModifierKeyCode::RightHyper) => keys::RIGHT_HYPER.len(), - KeyCode::Modifier(ModifierKeyCode::RightMeta) => keys::RIGHT_META.len(), - KeyCode::Modifier(ModifierKeyCode::IsoLevel3Shift) => keys::ISO_LEVEL_3_SHIFT.len(), - KeyCode::Modifier(ModifierKeyCode::IsoLevel5Shift) => keys::ISO_LEVEL_5_SHIFT.len(), }; if self.modifiers.contains(KeyModifiers::SHIFT) { width += 2; @@ -317,10 +141,6 @@ impl UnicodeWidthStr for KeyEvent { if self.modifiers.contains(KeyModifiers::CONTROL) { width += 2; } - if self.modifiers.contains(KeyModifiers::SUPER) { - // "-Meta" - width += 5; - } width } @@ -334,7 +154,7 @@ impl std::str::FromStr for KeyEvent { fn from_str(s: &str) -> Result<Self, Self::Err> { let mut tokens: Vec<_> = s.split('-').collect(); - let mut code = match tokens.pop().ok_or_else(|| anyhow!("Missing key code"))? { + let code = match tokens.pop().ok_or_else(|| anyhow!("Missing key code"))? { keys::BACKSPACE => KeyCode::Backspace, keys::ENTER => KeyCode::Enter, keys::LEFT => KeyCode::Left, @@ -346,73 +166,26 @@ impl std::str::FromStr for KeyEvent { keys::PAGEUP => KeyCode::PageUp, keys::PAGEDOWN => KeyCode::PageDown, keys::TAB => KeyCode::Tab, + keys::BACKTAB => KeyCode::BackTab, keys::DELETE => KeyCode::Delete, keys::INSERT => KeyCode::Insert, keys::NULL => KeyCode::Null, keys::ESC => KeyCode::Esc, keys::SPACE => KeyCode::Char(' '), - keys::MINUS => KeyCode::Char('-'), keys::LESS_THAN => KeyCode::Char('<'), keys::GREATER_THAN => KeyCode::Char('>'), - keys::CAPS_LOCK => KeyCode::CapsLock, - keys::SCROLL_LOCK => KeyCode::ScrollLock, - keys::NUM_LOCK => KeyCode::NumLock, - keys::PRINT_SCREEN => KeyCode::PrintScreen, - keys::PAUSE => KeyCode::Pause, - keys::MENU => KeyCode::Menu, - keys::KEYPAD_BEGIN => KeyCode::KeypadBegin, - keys::PLAY => KeyCode::Media(MediaKeyCode::Play), - keys::PAUSE_MEDIA => KeyCode::Media(MediaKeyCode::Pause), - keys::PLAY_PAUSE => KeyCode::Media(MediaKeyCode::PlayPause), - keys::STOP => KeyCode::Media(MediaKeyCode::Stop), - keys::REVERSE => KeyCode::Media(MediaKeyCode::Reverse), - keys::FAST_FORWARD => KeyCode::Media(MediaKeyCode::FastForward), - keys::REWIND => KeyCode::Media(MediaKeyCode::Rewind), - keys::TRACK_NEXT => KeyCode::Media(MediaKeyCode::TrackNext), - keys::TRACK_PREVIOUS => KeyCode::Media(MediaKeyCode::TrackPrevious), - keys::RECORD => KeyCode::Media(MediaKeyCode::Record), - keys::LOWER_VOLUME => KeyCode::Media(MediaKeyCode::LowerVolume), - keys::RAISE_VOLUME => KeyCode::Media(MediaKeyCode::RaiseVolume), - keys::MUTE_VOLUME => KeyCode::Media(MediaKeyCode::MuteVolume), - keys::LEFT_SHIFT => KeyCode::Modifier(ModifierKeyCode::LeftShift), - keys::LEFT_CONTROL => KeyCode::Modifier(ModifierKeyCode::LeftControl), - keys::LEFT_ALT => KeyCode::Modifier(ModifierKeyCode::LeftAlt), - keys::LEFT_SUPER => KeyCode::Modifier(ModifierKeyCode::LeftSuper), - keys::LEFT_HYPER => KeyCode::Modifier(ModifierKeyCode::LeftHyper), - keys::LEFT_META => KeyCode::Modifier(ModifierKeyCode::LeftMeta), - keys::RIGHT_SHIFT => KeyCode::Modifier(ModifierKeyCode::RightShift), - keys::RIGHT_CONTROL => KeyCode::Modifier(ModifierKeyCode::RightControl), - keys::RIGHT_ALT => KeyCode::Modifier(ModifierKeyCode::RightAlt), - keys::RIGHT_SUPER => KeyCode::Modifier(ModifierKeyCode::RightSuper), - keys::RIGHT_HYPER => KeyCode::Modifier(ModifierKeyCode::RightHyper), - keys::RIGHT_META => KeyCode::Modifier(ModifierKeyCode::RightMeta), - keys::ISO_LEVEL_3_SHIFT => KeyCode::Modifier(ModifierKeyCode::IsoLevel3Shift), - keys::ISO_LEVEL_5_SHIFT => KeyCode::Modifier(ModifierKeyCode::IsoLevel5Shift), + keys::PLUS => KeyCode::Char('+'), + keys::MINUS => KeyCode::Char('-'), + keys::SEMICOLON => KeyCode::Char(';'), + keys::PERCENT => KeyCode::Char('%'), single if single.chars().count() == 1 => KeyCode::Char(single.chars().next().unwrap()), function if function.len() > 1 && function.starts_with('F') => { let function: String = function.chars().skip(1).collect(); let function = str::parse::<u8>(&function)?; - (function > 0 && function < 25) - .then_some(KeyCode::F(function)) + (function > 0 && function < 13) + .then(|| KeyCode::F(function)) .ok_or_else(|| anyhow!("Invalid function key '{}'", function))? } - // Checking that the last token is empty ensures that this branch is only taken if - // `-` is used as a code. For example this branch will not be taken for `S-` (which is - // missing a code). - _ if s.ends_with('-') && tokens.last().is_some_and(|t| t.is_empty()) => { - if s == "-" { - return Ok(KeyEvent { - code: KeyCode::Char('-'), - modifiers: KeyModifiers::empty(), - }); - } else { - let suggestion = format!("{}-{}", s.trim_end_matches('-'), keys::MINUS); - return Err(anyhow!( - "Key '-' cannot be used with modifiers, use '{}' instead", - suggestion - )); - } - } invalid => return Err(anyhow!("Invalid key code '{}'", invalid)), }; @@ -422,7 +195,6 @@ impl std::str::FromStr for KeyEvent { "S" => KeyModifiers::SHIFT, "A" => KeyModifiers::ALT, "C" => KeyModifiers::CONTROL, - "Meta" | "Cmd" | "Win" => KeyModifiers::SUPER, _ => return Err(anyhow!("Invalid key modifier '{}-'", token)), }; @@ -432,18 +204,6 @@ impl std::str::FromStr for KeyEvent { modifiers.insert(flag); } - // Normalize character keys so that characters like C-S-r and C-R - // are represented by equal KeyEvents. - match code { - KeyCode::Char(ch) - if ch.is_ascii_lowercase() && modifiers.contains(KeyModifiers::SHIFT) => - { - code = KeyCode::Char(ch.to_ascii_uppercase()); - modifiers.remove(KeyModifiers::SHIFT); - } - _ => (), - } - Ok(KeyEvent { code, modifiers }) } } @@ -459,258 +219,15 @@ impl<'de> Deserialize<'de> for KeyEvent { } #[cfg(feature = "term")] -impl From<termina::event::Event> for Event { - fn from(event: termina::event::Event) -> Self { - match event { - termina::event::Event::Key(key) => Self::Key(key.into()), - termina::event::Event::Mouse(mouse) => Self::Mouse(mouse.into()), - termina::event::Event::WindowResized(termina::WindowSize { rows, cols, .. }) => { - Self::Resize(cols, rows) - } - termina::event::Event::FocusIn => Self::FocusGained, - termina::event::Event::FocusOut => Self::FocusLost, - termina::event::Event::Paste(s) => Self::Paste(s), - _ => unreachable!(), - } - } -} - -#[cfg(feature = "term")] -impl From<termina::event::MouseEvent> for MouseEvent { - fn from( - termina::event::MouseEvent { - kind, - column, - row, - modifiers, - }: termina::event::MouseEvent, - ) -> Self { - Self { - kind: kind.into(), - column, - row, - modifiers: modifiers.into(), - } - } -} - -#[cfg(feature = "term")] -impl From<termina::event::MouseEventKind> for MouseEventKind { - fn from(kind: termina::event::MouseEventKind) -> Self { - match kind { - termina::event::MouseEventKind::Down(button) => Self::Down(button.into()), - termina::event::MouseEventKind::Up(button) => Self::Up(button.into()), - termina::event::MouseEventKind::Drag(button) => Self::Drag(button.into()), - termina::event::MouseEventKind::Moved => Self::Moved, - termina::event::MouseEventKind::ScrollDown => Self::ScrollDown, - termina::event::MouseEventKind::ScrollUp => Self::ScrollUp, - termina::event::MouseEventKind::ScrollLeft => Self::ScrollLeft, - termina::event::MouseEventKind::ScrollRight => Self::ScrollRight, - } - } -} - -#[cfg(feature = "term")] -impl From<termina::event::MouseButton> for MouseButton { - fn from(button: termina::event::MouseButton) -> Self { - match button { - termina::event::MouseButton::Left => MouseButton::Left, - termina::event::MouseButton::Right => MouseButton::Right, - termina::event::MouseButton::Middle => MouseButton::Middle, - } - } -} - -#[cfg(feature = "term")] -impl From<termina::event::KeyEvent> for KeyEvent { - fn from( - termina::event::KeyEvent { - code, modifiers, .. - }: termina::event::KeyEvent, - ) -> Self { - if code == termina::event::KeyCode::BackTab { - // special case for BackTab -> Shift-Tab - let mut modifiers: KeyModifiers = modifiers.into(); - modifiers.insert(KeyModifiers::SHIFT); - Self { - code: KeyCode::Tab, - modifiers, - } - } else { - Self { - code: code.into(), - modifiers: modifiers.into(), - } - } - } -} - -#[cfg(feature = "term")] -impl From<KeyEvent> for termina::event::KeyEvent { - fn from(KeyEvent { code, modifiers }: KeyEvent) -> Self { - if code == KeyCode::Tab && modifiers.contains(KeyModifiers::SHIFT) { - // special case for Shift-Tab -> BackTab - let mut modifiers = modifiers; - modifiers.remove(KeyModifiers::SHIFT); - termina::event::KeyEvent { - code: termina::event::KeyCode::BackTab, - modifiers: modifiers.into(), - kind: termina::event::KeyEventKind::Press, - state: termina::event::KeyEventState::NONE, - } - } else { - termina::event::KeyEvent { - code: code.into(), - modifiers: modifiers.into(), - kind: termina::event::KeyEventKind::Press, - state: termina::event::KeyEventState::NONE, - } - } - } -} - -#[cfg(all(feature = "term", windows))] -impl From<crossterm::event::Event> for Event { - fn from(event: crossterm::event::Event) -> Self { - match event { - crossterm::event::Event::Key(key) => Self::Key(key.into()), - crossterm::event::Event::Mouse(mouse) => Self::Mouse(mouse.into()), - crossterm::event::Event::Resize(w, h) => Self::Resize(w, h), - crossterm::event::Event::FocusGained => Self::FocusGained, - crossterm::event::Event::FocusLost => Self::FocusLost, - crossterm::event::Event::Paste(s) => Self::Paste(s), - } - } -} - -#[cfg(all(feature = "term", windows))] -impl From<crossterm::event::MouseEvent> for MouseEvent { - fn from( - crossterm::event::MouseEvent { - kind, - column, - row, - modifiers, - }: crossterm::event::MouseEvent, - ) -> Self { - Self { - kind: kind.into(), - column, - row, - modifiers: modifiers.into(), - } - } -} - -#[cfg(all(feature = "term", windows))] -impl From<crossterm::event::MouseEventKind> for MouseEventKind { - fn from(kind: crossterm::event::MouseEventKind) -> Self { - match kind { - crossterm::event::MouseEventKind::Down(button) => Self::Down(button.into()), - crossterm::event::MouseEventKind::Up(button) => Self::Up(button.into()), - crossterm::event::MouseEventKind::Drag(button) => Self::Drag(button.into()), - crossterm::event::MouseEventKind::Moved => Self::Moved, - crossterm::event::MouseEventKind::ScrollDown => Self::ScrollDown, - crossterm::event::MouseEventKind::ScrollUp => Self::ScrollUp, - crossterm::event::MouseEventKind::ScrollLeft => Self::ScrollLeft, - crossterm::event::MouseEventKind::ScrollRight => Self::ScrollRight, - } - } -} - -#[cfg(all(feature = "term", windows))] -impl From<crossterm::event::MouseButton> for MouseButton { - fn from(button: crossterm::event::MouseButton) -> Self { - match button { - crossterm::event::MouseButton::Left => MouseButton::Left, - crossterm::event::MouseButton::Right => MouseButton::Right, - crossterm::event::MouseButton::Middle => MouseButton::Middle, - } - } -} - -#[cfg(all(feature = "term", windows))] impl From<crossterm::event::KeyEvent> for KeyEvent { fn from( - crossterm::event::KeyEvent { - code, modifiers, .. - }: crossterm::event::KeyEvent, - ) -> Self { - if code == crossterm::event::KeyCode::BackTab { - // special case for BackTab -> Shift-Tab - let mut modifiers: KeyModifiers = modifiers.into(); - modifiers.insert(KeyModifiers::SHIFT); - Self { - code: KeyCode::Tab, - modifiers, - } - } else { - Self { - code: code.into(), - modifiers: modifiers.into(), - } - } - } -} - -#[cfg(all(feature = "term", windows))] -impl From<KeyEvent> for crossterm::event::KeyEvent { - fn from(KeyEvent { code, modifiers }: KeyEvent) -> Self { - if code == KeyCode::Tab && modifiers.contains(KeyModifiers::SHIFT) { - // special case for Shift-Tab -> BackTab - let mut modifiers = modifiers; - modifiers.remove(KeyModifiers::SHIFT); - crossterm::event::KeyEvent { - code: crossterm::event::KeyCode::BackTab, - modifiers: modifiers.into(), - kind: crossterm::event::KeyEventKind::Press, - state: crossterm::event::KeyEventState::NONE, - } - } else { - crossterm::event::KeyEvent { - code: code.into(), - modifiers: modifiers.into(), - kind: crossterm::event::KeyEventKind::Press, - state: crossterm::event::KeyEventState::NONE, - } - } - } -} -pub fn parse_macro(keys_str: &str) -> anyhow::Result<Vec<KeyEvent>> { - use anyhow::Context; - let mut keys_res: anyhow::Result<_> = Ok(Vec::new()); - let mut i = 0; - while let Ok(keys) = &mut keys_res { - if i >= keys_str.len() { - break; - } - if !keys_str.is_char_boundary(i) { - i += 1; - continue; - } - - let s = &keys_str[i..]; - let mut end_i = 1; - while !s.is_char_boundary(end_i) { - end_i += 1; - } - let c = &s[..end_i]; - if c == ">" { - keys_res = Err(anyhow!("Unmatched '>'")); - } else if c != "<" { - keys.push(if c == "-" { keys::MINUS } else { c }); - i += end_i; - } else { - match s.find('>').context("'>' expected") { - Ok(end_i) => { - keys.push(&s[1..end_i]); - i += end_i + 1; - } - Err(err) => keys_res = Err(err), - } + crossterm::event::KeyEvent { code, modifiers }: crossterm::event::KeyEvent, + ) -> KeyEvent { + KeyEvent { + code: code.into(), + modifiers: modifiers.into(), } } - keys_res.and_then(|keys| keys.into_iter().map(str::parse).collect()) } #[cfg(test)] @@ -758,53 +275,6 @@ mod test { modifiers: KeyModifiers::NONE } ); - - assert_eq!( - str::parse::<KeyEvent>("%").unwrap(), - KeyEvent { - code: KeyCode::Char('%'), - modifiers: KeyModifiers::NONE - } - ); - - assert_eq!( - str::parse::<KeyEvent>(";").unwrap(), - KeyEvent { - code: KeyCode::Char(';'), - modifiers: KeyModifiers::NONE - } - ); - - assert_eq!( - str::parse::<KeyEvent>(">").unwrap(), - KeyEvent { - code: KeyCode::Char('>'), - modifiers: KeyModifiers::NONE - } - ); - - assert_eq!( - str::parse::<KeyEvent>("<").unwrap(), - KeyEvent { - code: KeyCode::Char('<'), - modifiers: KeyModifiers::NONE - } - ); - - assert_eq!( - str::parse::<KeyEvent>("+").unwrap(), - KeyEvent { - code: KeyCode::Char('+'), - modifiers: KeyModifiers::NONE - } - ); - assert_eq!( - str::parse::<KeyEvent>("-").unwrap(), - KeyEvent { - code: KeyCode::Char('-'), - modifiers: KeyModifiers::NONE, - } - ); } #[test] @@ -832,54 +302,11 @@ mod test { modifiers: KeyModifiers::SHIFT | KeyModifiers::CONTROL } ); - - assert_eq!( - str::parse::<KeyEvent>("A-C-+").unwrap(), - KeyEvent { - code: KeyCode::Char('+'), - modifiers: KeyModifiers::ALT | KeyModifiers::CONTROL - } - ); - - assert_eq!( - str::parse::<KeyEvent>("C-S-r").unwrap(), - str::parse::<KeyEvent>("C-R").unwrap(), - ); - - assert_eq!( - str::parse::<KeyEvent>("S-w").unwrap(), - KeyEvent { - code: KeyCode::Char('W'), - modifiers: KeyModifiers::NONE - } - ); - - assert_eq!( - str::parse::<KeyEvent>("Meta-c").unwrap(), - KeyEvent { - code: KeyCode::Char('c'), - modifiers: KeyModifiers::SUPER - } - ); - assert_eq!( - str::parse::<KeyEvent>("Win-s").unwrap(), - KeyEvent { - code: KeyCode::Char('s'), - modifiers: KeyModifiers::SUPER - } - ); - assert_eq!( - str::parse::<KeyEvent>("Cmd-d").unwrap(), - KeyEvent { - code: KeyCode::Char('d'), - modifiers: KeyModifiers::SUPER - } - ); } #[test] fn parsing_nonsensical_keys_fails() { - assert!(str::parse::<KeyEvent>("F25").is_err()); + assert!(str::parse::<KeyEvent>("F13").is_err()); assert!(str::parse::<KeyEvent>("F0").is_err()); assert!(str::parse::<KeyEvent>("aaa").is_err()); assert!(str::parse::<KeyEvent>("S-S-a").is_err()); @@ -887,188 +314,5 @@ mod test { assert!(str::parse::<KeyEvent>("FU").is_err()); assert!(str::parse::<KeyEvent>("123").is_err()); assert!(str::parse::<KeyEvent>("S--").is_err()); - assert!(str::parse::<KeyEvent>("S-").is_err()); - assert!(str::parse::<KeyEvent>("S-percent").is_err()); - } - - #[test] - fn parsing_unsupported_named_keys() { - assert!(str::parse::<KeyEvent>("plus").is_err()); - assert!(str::parse::<KeyEvent>("percent").is_err()); - assert!(str::parse::<KeyEvent>("semicolon").is_err()); - } - - #[test] - fn parsing_valid_macros() { - assert_eq!( - parse_macro("xdo").ok(), - Some(vec![ - KeyEvent { - code: KeyCode::Char('x'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('d'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('o'), - modifiers: KeyModifiers::NONE, - }, - ]), - ); - - assert_eq!( - parse_macro("<C-w>v<C-w>h<C-o>xx<A-s>").ok(), - Some(vec![ - KeyEvent { - code: KeyCode::Char('w'), - modifiers: KeyModifiers::CONTROL, - }, - KeyEvent { - code: KeyCode::Char('v'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('w'), - modifiers: KeyModifiers::CONTROL, - }, - KeyEvent { - code: KeyCode::Char('h'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('o'), - modifiers: KeyModifiers::CONTROL, - }, - KeyEvent { - code: KeyCode::Char('x'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('x'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('s'), - modifiers: KeyModifiers::ALT, - }, - ]) - ); - - assert_eq!( - parse_macro(":o foo.bar<ret>").ok(), - Some(vec![ - KeyEvent { - code: KeyCode::Char(':'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('o'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char(' '), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('f'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('o'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('o'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('.'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('b'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('a'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('r'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Enter, - modifiers: KeyModifiers::NONE, - }, - ]) - ); - - assert_eq!( - parse_macro(":w aa-bb.txt<ret>").ok(), - Some(vec![ - KeyEvent { - code: KeyCode::Char(':'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('w'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char(' '), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('a'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('a'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('-'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('b'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('b'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('.'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('t'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('x'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Char('t'), - modifiers: KeyModifiers::NONE, - }, - KeyEvent { - code: KeyCode::Enter, - modifiers: KeyModifiers::NONE, - }, - ]) - ); - } - - #[test] - fn parsing_invalid_macros_fails() { - assert!(parse_macro("abc<C-").is_err()); - assert!(parse_macro("abc>123").is_err()); - assert!(parse_macro("wd<foo>").is_err()); } } |