/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see .
*/
use std::fmt::Display;
use regex::Regex;
lazy_static! {
static ref RAW_PARSER: Regex = Regex::new(r"^RAW\((\d+)\)$").unwrap();
}
#[derive(Debug, Clone)]
pub enum Key {
// Modifiers
Alt,
CapsLock,
Control,
Meta,
NumLock,
Shift,
// Whitespace
Enter,
Tab,
Space,
// Navigation
ArrowDown,
ArrowLeft,
ArrowRight,
ArrowUp,
End,
Home,
PageDown,
PageUp,
// UI
Escape,
// Editing keys
Backspace,
Insert,
Delete,
// Function keys
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
F13,
F14,
F15,
F16,
F17,
F18,
F19,
F20,
// Alphabet
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
// Numbers
N0,
N1,
N2,
N3,
N4,
N5,
N6,
N7,
N8,
N9,
// Numpad
Numpad0,
Numpad1,
Numpad2,
Numpad3,
Numpad4,
Numpad5,
Numpad6,
Numpad7,
Numpad8,
Numpad9,
// Specify the raw platform-specific virtual key code.
Raw(i32),
}
impl Display for Key {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Key::Alt => write!(f, "ALT"),
Key::CapsLock => write!(f, "CAPSLOCK"),
Key::Control => write!(f, "CTRL"),
Key::Meta => write!(f, "META"),
Key::NumLock => write!(f, "NUMLOCK"),
Key::Shift => write!(f, "SHIFT"),
Key::Enter => write!(f, "ENTER"),
Key::Tab => write!(f, "TAB"),
Key::Space => write!(f, "SPACE"),
Key::ArrowDown => write!(f, "DOWN"),
Key::ArrowLeft => write!(f, "LEFT"),
Key::ArrowRight => write!(f, "RIGHT"),
Key::ArrowUp => write!(f, "UP"),
Key::End => write!(f, "END"),
Key::Home => write!(f, "HOME"),
Key::PageDown => write!(f, "PAGEDOWN"),
Key::PageUp => write!(f, "PAGEUP"),
Key::Escape => write!(f, "ESC"),
Key::Backspace => write!(f, "BACKSPACE"),
Key::Insert => write!(f, "INSERT"),
Key::Delete => write!(f, "DELETE"),
Key::F1 => write!(f, "F1"),
Key::F2 => write!(f, "F2"),
Key::F3 => write!(f, "F3"),
Key::F4 => write!(f, "F4"),
Key::F5 => write!(f, "F5"),
Key::F6 => write!(f, "F6"),
Key::F7 => write!(f, "F7"),
Key::F8 => write!(f, "F8"),
Key::F9 => write!(f, "F9"),
Key::F10 => write!(f, "F10"),
Key::F11 => write!(f, "F11"),
Key::F12 => write!(f, "F12"),
Key::F13 => write!(f, "F13"),
Key::F14 => write!(f, "F14"),
Key::F15 => write!(f, "F15"),
Key::F16 => write!(f, "F16"),
Key::F17 => write!(f, "F17"),
Key::F18 => write!(f, "F18"),
Key::F19 => write!(f, "F19"),
Key::F20 => write!(f, "F20"),
Key::A => write!(f, "A"),
Key::B => write!(f, "B"),
Key::C => write!(f, "C"),
Key::D => write!(f, "D"),
Key::E => write!(f, "E"),
Key::F => write!(f, "F"),
Key::G => write!(f, "G"),
Key::H => write!(f, "H"),
Key::I => write!(f, "I"),
Key::J => write!(f, "J"),
Key::K => write!(f, "K"),
Key::L => write!(f, "L"),
Key::M => write!(f, "M"),
Key::N => write!(f, "N"),
Key::O => write!(f, "O"),
Key::P => write!(f, "P"),
Key::Q => write!(f, "Q"),
Key::R => write!(f, "R"),
Key::S => write!(f, "S"),
Key::T => write!(f, "T"),
Key::U => write!(f, "U"),
Key::V => write!(f, "V"),
Key::W => write!(f, "W"),
Key::X => write!(f, "X"),
Key::Y => write!(f, "Y"),
Key::Z => write!(f, "Z"),
Key::N0 => write!(f, "0"),
Key::N1 => write!(f, "1"),
Key::N2 => write!(f, "2"),
Key::N3 => write!(f, "3"),
Key::N4 => write!(f, "4"),
Key::N5 => write!(f, "5"),
Key::N6 => write!(f, "6"),
Key::N7 => write!(f, "7"),
Key::N8 => write!(f, "8"),
Key::N9 => write!(f, "9"),
Key::Numpad0 => write!(f, "NUMPAD0"),
Key::Numpad1 => write!(f, "NUMPAD1"),
Key::Numpad2 => write!(f, "NUMPAD2"),
Key::Numpad3 => write!(f, "NUMPAD3"),
Key::Numpad4 => write!(f, "NUMPAD4"),
Key::Numpad5 => write!(f, "NUMPAD5"),
Key::Numpad6 => write!(f, "NUMPAD6"),
Key::Numpad7 => write!(f, "NUMPAD7"),
Key::Numpad8 => write!(f, "NUMPAD8"),
Key::Numpad9 => write!(f, "NUMPAD9"),
Key::Raw(code) => write!(f, "RAW({})", code),
}
}
}
impl Key {
pub fn parse(key: &str) -> Option {
let parsed = match key {
"ALT" | "OPTION" => Some(Key::Alt),
"CAPSLOCK" => Some(Key::CapsLock),
"CTRL" => Some(Key::Control),
"META" | "CMD" => Some(Key::Meta),
"NUMLOCK" => Some(Key::NumLock),
"SHIFT" => Some(Key::Shift),
"ENTER" => Some(Key::Enter),
"TAB" => Some(Key::Tab),
"SPACE" => Some(Key::Space),
"DOWN" => Some(Key::ArrowDown),
"LEFT" => Some(Key::ArrowLeft),
"RIGHT" => Some(Key::ArrowRight),
"UP" => Some(Key::ArrowUp),
"END" => Some(Key::End),
"HOME" => Some(Key::Home),
"PAGEDOWN" => Some(Key::PageDown),
"PAGEUP" => Some(Key::PageUp),
"ESC" => Some(Key::Escape),
"BACKSPACE" => Some(Key::Backspace),
"INSERT" => Some(Key::Insert),
"DELETE" => Some(Key::Delete),
"F1" => Some(Key::F1),
"F2" => Some(Key::F2),
"F3" => Some(Key::F3),
"F4" => Some(Key::F4),
"F5" => Some(Key::F5),
"F6" => Some(Key::F6),
"F7" => Some(Key::F7),
"F8" => Some(Key::F8),
"F9" => Some(Key::F9),
"F10" => Some(Key::F10),
"F11" => Some(Key::F11),
"F12" => Some(Key::F12),
"F13" => Some(Key::F13),
"F14" => Some(Key::F14),
"F15" => Some(Key::F15),
"F16" => Some(Key::F16),
"F17" => Some(Key::F17),
"F18" => Some(Key::F18),
"F19" => Some(Key::F19),
"F20" => Some(Key::F20),
"A" => Some(Key::A),
"B" => Some(Key::B),
"C" => Some(Key::C),
"D" => Some(Key::D),
"E" => Some(Key::E),
"F" => Some(Key::F),
"G" => Some(Key::G),
"H" => Some(Key::H),
"I" => Some(Key::I),
"J" => Some(Key::J),
"K" => Some(Key::K),
"L" => Some(Key::L),
"M" => Some(Key::M),
"N" => Some(Key::N),
"O" => Some(Key::O),
"P" => Some(Key::P),
"Q" => Some(Key::Q),
"R" => Some(Key::R),
"S" => Some(Key::S),
"T" => Some(Key::T),
"U" => Some(Key::U),
"V" => Some(Key::V),
"W" => Some(Key::W),
"X" => Some(Key::X),
"Y" => Some(Key::Y),
"Z" => Some(Key::Z),
"0" => Some(Key::N0),
"1" => Some(Key::N1),
"2" => Some(Key::N2),
"3" => Some(Key::N3),
"4" => Some(Key::N4),
"5" => Some(Key::N5),
"6" => Some(Key::N6),
"7" => Some(Key::N7),
"8" => Some(Key::N8),
"9" => Some(Key::N9),
"NUMPAD0" => Some(Key::Numpad0),
"NUMPAD1" => Some(Key::Numpad1),
"NUMPAD2" => Some(Key::Numpad2),
"NUMPAD3" => Some(Key::Numpad3),
"NUMPAD4" => Some(Key::Numpad4),
"NUMPAD5" => Some(Key::Numpad5),
"NUMPAD6" => Some(Key::Numpad6),
"NUMPAD7" => Some(Key::Numpad7),
"NUMPAD8" => Some(Key::Numpad8),
"NUMPAD9" => Some(Key::Numpad9),
_ => None,
};
if parsed.is_none() {
// Attempt to parse raw keys
if RAW_PARSER.is_match(key) {
if let Some(caps) = RAW_PARSER.captures(key) {
let code_str = caps.get(1).map_or("", |m| m.as_str());
let code = code_str.parse::();
if let Ok(code) = code {
return Some(Key::Raw(code));
}
}
}
}
parsed
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_works_correctly() {
assert!(matches!(Key::parse("ALT").unwrap(), Key::Alt));
assert!(matches!(Key::parse("RAW(1234)").unwrap(), Key::Raw(1234)));
}
#[test]
fn parse_invalid_keys() {
assert!(Key::parse("INVALID").is_none());
assert!(Key::parse("RAW(a)").is_none());
}
}