use std::fmt;
use bitflags::bitflags;
use zng_txt::{ToTxt, Txt};
use zng_var::{impl_from_and_into_var, BoxedVar, Var};
#[doc(hidden)]
pub use zng_view_api::keyboard::{Key, KeyCode};
use crate::event::{Command, CommandMetaVar, StaticCommandMetaVarId};
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum GestureKey {
    Key(Key),
    Code(KeyCode),
}
impl std::hash::Hash for GestureKey {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        core::mem::discriminant(self).hash(state);
        match self {
            GestureKey::Key(k) => match k {
                Key::Char(c) => {
                    for c in c.to_uppercase() {
                        c.hash(state);
                    }
                }
                Key::Str(s) => {
                    unicase::UniCase::new(s).hash(state);
                }
                k => k.hash(state),
            },
            GestureKey::Code(c) => c.hash(state),
        }
    }
}
impl Eq for GestureKey {}
impl PartialEq for GestureKey {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Self::Key(l0), Self::Key(r0)) => match (l0, r0) {
                (Key::Char(l), Key::Char(r)) => {
                    let mut l = l.to_uppercase();
                    let mut r = r.to_uppercase();
                    while let (Some(l), Some(r)) = (l.next(), r.next()) {
                        if l != r {
                            return false;
                        }
                    }
                    l.next().is_none() && r.next().is_none()
                }
                (Key::Str(l), Key::Str(r)) => unicase::eq(l, r),
                (l0, r0) => l0 == r0,
            },
            (Self::Code(l0), Self::Code(r0)) => l0 == r0,
            _ => false,
        }
    }
}
impl fmt::Display for GestureKey {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            GestureKey::Key(k) => match k {
                Key::Char(c) => write!(f, "{}", c),
                Key::Str(s) => write!(f, "{}", s),
                k => write!(f, "{k:?}"),
            },
            GestureKey::Code(c) => write!(f, "{:?}", c),
        }
    }
}
impl TryFrom<Key> for GestureKey {
    type Error = Key;
    fn try_from(key: Key) -> Result<Self, Self::Error> {
        if key.is_modifier() || key.is_composition() || key == Key::Unidentified {
            Err(key)
        } else {
            Ok(Self::Key(key))
        }
    }
}
impl TryFrom<KeyCode> for GestureKey {
    type Error = KeyCode;
    fn try_from(key: KeyCode) -> Result<Self, Self::Error> {
        if key.is_modifier() || key.is_composition() || key.is_unidentified() {
            Err(key)
        } else {
            Ok(Self::Code(key))
        }
    }
}
impl std::str::FromStr for GestureKey {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match Key::from_str(s) {
            Key::Str(s) => match KeyCode::from_str(&s) {
                Ok(k) => {
                    let key = k
                        .try_into()
                        .map_err(|e| ParseError::new(format!("key `{e:?}` cannot be used in gestures")))?;
                    Ok(key)
                }
                Err(_) => Ok(Self::Key(Key::Str(s))),
            },
            k => {
                let key = k
                    .try_into()
                    .map_err(|e| ParseError::new(format!("key `{e:?}` cannot be used in gestures")))?;
                Ok(key)
            }
        }
    }
}
#[derive(Clone, serde::Serialize, serde::Deserialize)]
pub struct KeyGesture {
    pub modifiers: ModifiersState,
    pub key: GestureKey,
}
impl PartialEq for KeyGesture {
    fn eq(&self, other: &Self) -> bool {
        self.modifiers.ambit() == other.modifiers.ambit() && self.key == other.key
    }
}
impl Eq for KeyGesture {}
impl std::hash::Hash for KeyGesture {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.modifiers.ambit().hash(state);
        self.key.hash(state);
    }
}
impl KeyGesture {
    #[allow(missing_docs)]
    pub fn new(modifiers: ModifiersState, key: GestureKey) -> Self {
        KeyGesture { modifiers, key }
    }
    pub fn new_key(key: GestureKey) -> Self {
        KeyGesture {
            modifiers: ModifiersState::empty(),
            key,
        }
    }
}
impl fmt::Debug for KeyGesture {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            f.debug_struct("KeyGesture")
                .field("modifiers", &self.modifiers)
                .field("key", &self.key)
                .finish()
        } else {
            write!(f, "{self}")
        }
    }
}
impl fmt::Display for KeyGesture {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.modifiers.has_super() {
            write!(f, "Super+")?
        }
        if self.modifiers.has_ctrl() {
            write!(f, "Ctrl+")?
        }
        if self.modifiers.has_shift() {
            write!(f, "Shift+")?
        }
        if self.modifiers.has_alt() {
            write!(f, "Alt+")?
        }
        write!(f, "{}", self.key)
    }
}
#[derive(Clone, Copy, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
pub enum ModifierGesture {
    Super,
    Ctrl,
    Shift,
    Alt,
}
impl fmt::Debug for ModifierGesture {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            write!(f, "ModifierGesture::")?;
        }
        write!(f, "{self}")
    }
}
impl fmt::Display for ModifierGesture {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ModifierGesture::Super => write!(f, "Super"),
            ModifierGesture::Ctrl => write!(f, "Ctrl"),
            ModifierGesture::Shift => write!(f, "Shift"),
            ModifierGesture::Alt => write!(f, "Alt"),
        }
    }
}
impl<'a> TryFrom<&'a Key> for ModifierGesture {
    type Error = &'a Key;
    fn try_from(value: &'a Key) -> Result<Self, Self::Error> {
        match value {
            Key::Alt | Key::AltGraph => Ok(ModifierGesture::Alt),
            Key::Ctrl => Ok(ModifierGesture::Ctrl),
            Key::Shift => Ok(ModifierGesture::Shift),
            Key::Super => Ok(ModifierGesture::Super),
            key => Err(key),
        }
    }
}
impl TryFrom<KeyCode> for ModifierGesture {
    type Error = KeyCode;
    fn try_from(value: KeyCode) -> Result<Self, Self::Error> {
        match value {
            KeyCode::AltLeft | KeyCode::AltRight => Ok(ModifierGesture::Alt),
            KeyCode::CtrlLeft | KeyCode::CtrlRight => Ok(ModifierGesture::Ctrl),
            KeyCode::ShiftLeft | KeyCode::ShiftRight => Ok(ModifierGesture::Shift),
            KeyCode::SuperLeft | KeyCode::SuperRight => Ok(ModifierGesture::Super),
            key => Err(key),
        }
    }
}
impl ModifierGesture {
    pub fn left_key(&self) -> (KeyCode, Key) {
        match self {
            ModifierGesture::Super => (KeyCode::SuperLeft, Key::Super),
            ModifierGesture::Ctrl => (KeyCode::CtrlLeft, Key::Ctrl),
            ModifierGesture::Shift => (KeyCode::ShiftLeft, Key::Shift),
            ModifierGesture::Alt => (KeyCode::AltLeft, Key::Alt),
        }
    }
    pub fn modifiers_state(&self) -> ModifiersState {
        match self {
            ModifierGesture::Super => ModifiersState::LOGO,
            ModifierGesture::Ctrl => ModifiersState::CTRL,
            ModifierGesture::Shift => ModifiersState::SHIFT,
            ModifierGesture::Alt => ModifiersState::ALT,
        }
    }
}
#[derive(Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct KeyChord {
    pub starter: KeyGesture,
    pub complement: KeyGesture,
}
impl fmt::Debug for KeyChord {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            f.debug_struct("KeyChord")
                .field("starter", &self.starter)
                .field("complement", &self.complement)
                .finish()
        } else {
            write!(f, "{self}")
        }
    }
}
impl fmt::Display for KeyChord {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} {}", self.starter, self.complement)
    }
}
#[derive(Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum Shortcut {
    Gesture(KeyGesture),
    Chord(KeyChord),
    Modifier(ModifierGesture),
}
impl fmt::Debug for Shortcut {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            match self {
                Shortcut::Gesture(g) => f.debug_tuple("Shortcut::Gesture").field(g).finish(),
                Shortcut::Chord(c) => f.debug_tuple("Shortcut::Chord").field(c).finish(),
                Shortcut::Modifier(m) => f.debug_tuple("Shortcut::Modifier").field(m).finish(),
            }
        } else {
            write!(f, "{self}")
        }
    }
}
impl fmt::Display for Shortcut {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Shortcut::Gesture(g) => fmt::Display::fmt(g, f),
            Shortcut::Chord(c) => fmt::Display::fmt(c, f),
            Shortcut::Modifier(m) => fmt::Display::fmt(m, f),
        }
    }
}
impl Shortcut {
    pub fn modifiers_state(&self) -> ModifiersState {
        match self {
            Shortcut::Gesture(g) => g.modifiers,
            Shortcut::Chord(c) => c.complement.modifiers,
            Shortcut::Modifier(m) => m.modifiers_state(),
        }
    }
}
impl_from_and_into_var! {
    fn from(shortcut: Shortcut) -> Shortcuts {
        Shortcuts(vec![shortcut])
    }
    fn from(key_gesture: KeyGesture) -> Shortcut {
        Shortcut::Gesture(key_gesture)
    }
    fn from(key_chord: KeyChord) -> Shortcut {
        Shortcut::Chord(key_chord)
    }
    fn from(modifier: ModifierGesture) -> Shortcut {
        Shortcut::Modifier(modifier)
    }
    fn from(gesture_key: GestureKey) -> Shortcut {
        KeyGesture::new_key(gesture_key).into()
    }
    fn from(gesture_key: GestureKey) -> Shortcuts {
        Shortcuts(vec![gesture_key.into()])
    }
    fn from(key_gesture: KeyGesture) -> Shortcuts {
        Shortcuts(vec![key_gesture.into()])
    }
    fn from(key_chord: KeyChord) -> Shortcuts {
        Shortcuts(vec![key_chord.into()])
    }
    fn from(modifier: ModifierGesture) -> Shortcuts {
        Shortcuts(vec![modifier.into()])
    }
    fn from(shortcuts: Vec<Shortcut>) -> Shortcuts {
        Shortcuts(shortcuts)
    }
}
impl<const N: usize> From<[Shortcut; N]> for Shortcuts {
    fn from(a: [Shortcut; N]) -> Self {
        Shortcuts(a.into())
    }
}
impl<const N: usize> crate::var::IntoVar<Shortcuts> for [Shortcut; N] {
    type Var = crate::var::LocalVar<Shortcuts>;
    fn into_var(self) -> Self::Var {
        crate::var::LocalVar(self.into())
    }
}
#[derive(Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Shortcuts(pub Vec<Shortcut>);
impl Shortcuts {
    pub const fn new() -> Self {
        Self(vec![])
    }
    pub fn from_char(character: char) -> Result<Self, char> {
        if character.is_control() {
            Err(character)
        } else {
            Ok(Self(vec![Shortcut::Gesture(KeyGesture {
                modifiers: ModifiersState::empty(),
                key: GestureKey::Key(Key::Char(character)),
            })]))
        }
    }
    pub fn contains(&self, shortcut: &Shortcut) -> bool {
        self.0.contains(shortcut)
    }
}
impl TryFrom<char> for Shortcuts {
    type Error = char;
    fn try_from(value: char) -> Result<Self, Self::Error> {
        Shortcuts::from_char(value)
    }
}
impl fmt::Debug for Shortcuts {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            f.debug_tuple("Shortcuts").field(&self.0).finish()
        } else {
            write!(f, "[")?;
            if !self.0.is_empty() {
                if let Shortcut::Chord(c) = &self.0[0] {
                    write!(f, "({c:?})")?;
                } else {
                    write!(f, "{:?}", self.0[0])?;
                }
                for shortcut in &self.0[1..] {
                    if let Shortcut::Chord(c) = shortcut {
                        write!(f, ", ({c:?})")?;
                    } else {
                        write!(f, ", {shortcut:?}")?;
                    }
                }
            }
            write!(f, "]")
        }
    }
}
impl fmt::Display for Shortcuts {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if !self.0.is_empty() {
            write!(f, "{}", self.0[0])?;
            for shortcut in &self.0[1..] {
                write!(f, " | {shortcut}")?;
            }
        }
        Ok(())
    }
}
impl std::ops::Deref for Shortcuts {
    type Target = Vec<Shortcut>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
impl std::ops::DerefMut for Shortcuts {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseError {
    pub error: String,
}
impl ParseError {
    #[allow(missing_docs)]
    pub fn new(error: impl ToString) -> Self {
        ParseError { error: error.to_string() }
    }
}
impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.error.fmt(f)
    }
}
impl std::error::Error for ParseError {}
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
    #[serde(transparent)]
    pub struct ModifiersState: u8 {
        const L_SHIFT = 0b0000_0001;
        const R_SHIFT = 0b0000_0010;
        const SHIFT   = 0b0000_0011;
        const L_CTRL = 0b0000_0100;
        const R_CTRL = 0b0000_1000;
        const CTRL   = 0b0000_1100;
        const L_ALT = 0b0001_0000;
        const R_ALT = 0b0010_0000;
        const ALT   = 0b0011_0000;
        const L_LOGO = 0b0100_0000;
        const R_LOGO = 0b1000_0000;
        const LOGO   = 0b1100_0000;
    }
}
impl ModifiersState {
    pub fn has_shift(self) -> bool {
        self.intersects(Self::SHIFT)
    }
    pub fn has_ctrl(self) -> bool {
        self.intersects(Self::CTRL)
    }
    pub fn has_alt(self) -> bool {
        self.intersects(Self::ALT)
    }
    pub fn has_super(self) -> bool {
        self.intersects(Self::LOGO)
    }
    pub fn is_only(self, part: ModifiersState) -> bool {
        !self.is_empty() && (self - part).is_empty()
    }
    pub fn is_only_shift(self) -> bool {
        self.is_only(ModifiersState::SHIFT)
    }
    pub fn is_only_ctrl(self) -> bool {
        self.is_only(ModifiersState::CTRL)
    }
    pub fn is_only_alt(self) -> bool {
        self.is_only(ModifiersState::ALT)
    }
    pub fn is_only_logo(self) -> bool {
        self.is_only(ModifiersState::LOGO)
    }
    pub fn take(&mut self, part: ModifiersState) -> bool {
        let r = self.intersects(part);
        if r {
            self.remove(part);
        }
        r
    }
    pub fn take_shift(&mut self) -> bool {
        self.take(ModifiersState::SHIFT)
    }
    pub fn take_ctrl(&mut self) -> bool {
        self.take(ModifiersState::CTRL)
    }
    pub fn take_alt(&mut self) -> bool {
        self.take(ModifiersState::ALT)
    }
    pub fn take_logo(&mut self) -> bool {
        self.take(ModifiersState::LOGO)
    }
    pub fn ambit(self) -> Self {
        let mut r = Self::empty();
        if self.has_alt() {
            r |= Self::ALT;
        }
        if self.has_ctrl() {
            r |= Self::CTRL;
        }
        if self.has_shift() {
            r |= Self::SHIFT;
        }
        if self.has_super() {
            r |= Self::LOGO;
        }
        r
    }
    pub fn into_alt(self) -> Self {
        self & Self::ALT
    }
    pub fn into_ctrl(self) -> Self {
        self & Self::CTRL
    }
    pub fn into_shift(self) -> Self {
        self & Self::SHIFT
    }
    pub fn into_logo(self) -> Self {
        self & Self::LOGO
    }
    pub fn from_code(code: KeyCode) -> ModifiersState {
        match code {
            KeyCode::AltLeft => Self::L_ALT,
            KeyCode::AltRight => Self::R_ALT,
            KeyCode::CtrlLeft => Self::L_CTRL,
            KeyCode::CtrlRight => Self::R_CTRL,
            KeyCode::ShiftLeft => Self::L_SHIFT,
            KeyCode::ShiftRight => Self::R_SHIFT,
            KeyCode::SuperLeft => Self::L_LOGO,
            KeyCode::SuperRight => Self::R_LOGO,
            _ => Self::empty(),
        }
    }
    pub fn from_key(key: Key) -> ModifiersState {
        match key {
            Key::Alt => Self::L_ALT,
            Key::AltGraph => Self::R_ALT,
            Key::Shift => Self::SHIFT,
            Key::Ctrl => Self::CTRL,
            Key::Super => Self::LOGO,
            _ => Self::empty(),
        }
    }
    pub fn codes(self) -> Vec<KeyCode> {
        let mut r = vec![];
        if self.contains(Self::L_LOGO) {
            r.push(KeyCode::SuperLeft);
        } else if self.contains(Self::R_LOGO) {
            r.push(KeyCode::SuperRight);
        }
        if self.contains(Self::L_CTRL) {
            r.push(KeyCode::CtrlLeft);
        } else if self.contains(Self::R_CTRL) {
            r.push(KeyCode::CtrlRight);
        }
        if self.contains(Self::L_SHIFT) {
            r.push(KeyCode::ShiftLeft);
        } else if self.contains(Self::R_SHIFT) {
            r.push(KeyCode::ShiftRight);
        }
        if self.contains(Self::L_ALT) {
            r.push(KeyCode::AltLeft);
        } else if self.contains(Self::R_ALT) {
            r.push(KeyCode::AltRight);
        }
        r
    }
    pub fn keys(self) -> Vec<Key> {
        let mut r = vec![];
        if self.intersects(Self::LOGO) {
            r.push(Key::Super);
        }
        if self.intersects(Self::CTRL) {
            r.push(Key::Ctrl);
        }
        if self.intersects(Self::SHIFT) {
            r.push(Key::Shift);
        }
        if self.contains(Self::R_ALT) {
            r.push(Key::AltGraph);
        } else if self.contains(Self::R_ALT) {
            r.push(Key::Alt);
        }
        r
    }
}
pub trait CommandShortcutExt {
    fn shortcut(self) -> CommandMetaVar<Shortcuts>;
    fn shortcut_txt(self) -> BoxedVar<Txt>
    where
        Self: Sized,
    {
        self.shortcut()
            .map(|c| if c.is_empty() { Txt::from("") } else { c[0].to_txt() })
            .boxed()
    }
    fn shortcut_filter(self) -> CommandMetaVar<ShortcutFilter>;
    fn init_shortcut(self, shortcut: impl Into<Shortcuts>) -> Self;
    fn init_shortcut_filter(self, filter: impl Into<ShortcutFilter>) -> Self;
}
impl CommandShortcutExt for Command {
    fn shortcut(self) -> CommandMetaVar<Shortcuts> {
        self.with_meta(|m| m.get_var_or_default(&COMMAND_SHORTCUT_ID))
    }
    fn shortcut_filter(self) -> CommandMetaVar<ShortcutFilter> {
        self.with_meta(|m| m.get_var_or_default(&COMMAND_SHORTCUT_FILTER_ID))
    }
    fn init_shortcut(self, shortcut: impl Into<Shortcuts>) -> Self {
        self.with_meta(|m| m.init_var(&COMMAND_SHORTCUT_ID, shortcut.into()));
        self
    }
    fn init_shortcut_filter(self, filter: impl Into<ShortcutFilter>) -> Self {
        self.with_meta(|m| m.init_var(&COMMAND_SHORTCUT_FILTER_ID, filter.into()));
        self
    }
}
bitflags! {
    #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
    #[serde(transparent)]
    pub struct ShortcutFilter: u8 {
        const ENABLED = 0b001;
        const FOCUSED = 0b010;
        const CMD_ENABLED = 0b100;
    }
}
static COMMAND_SHORTCUT_ID: StaticCommandMetaVarId<Shortcuts> = StaticCommandMetaVarId::new_unique();
static COMMAND_SHORTCUT_FILTER_ID: StaticCommandMetaVarId<ShortcutFilter> = StaticCommandMetaVarId::new_unique();
#[doc(hidden)]
#[macro_export]
macro_rules! __shortcut {
    (-> + $Key:tt) => {
        $crate::shortcut::KeyGesture {
            key: $crate::__shortcut!(@key $Key),
            modifiers: $crate::shortcut::ModifiersState::empty(),
        }
    };
    (-> $($MODIFIER:ident)|+ + $Key:tt) => {
        $crate::shortcut::KeyGesture {
            key: $crate::__shortcut!(@key $Key),
            modifiers: $($crate::shortcut::ModifiersState::$MODIFIER)|+,
        }
    };
    (=> $($STARTER_MODIFIER:ident)|* + $StarterKey:tt, $($COMPLEMENT_MODIFIER:ident)|* + $ComplementKey:tt) => {
        $crate::shortcut::KeyChord {
            starter: $crate::__shortcut!(-> $($STARTER_MODIFIER)|* + $StarterKey),
            complement: $crate::__shortcut!(-> $($COMPLEMENT_MODIFIER)|* + $ComplementKey)
        }
    };
    (@key $Key:ident) => { $crate::shortcut::GestureKey::Key($crate::shortcut::Key::$Key) };
    (@key $key_char:literal) => { $crate::shortcut::GestureKey::Key($crate::shortcut::Key::Char($key_char)) };
}
#[macro_export]
macro_rules! shortcut_macro {
    (Super) => {
        $crate::shortcut::Shortcut::Modifier($crate::shortcut::ModifierGesture::Super)
    };
    (Shift) => {
        $crate::shortcut::Shortcut::Modifier($crate::shortcut::ModifierGesture::Shift)
    };
    (Ctrl) => {
        $crate::shortcut::Shortcut::Modifier($crate::shortcut::ModifierGesture::Ctrl)
    };
    (Alt) => {
        $crate::shortcut::Shortcut::Modifier($crate::shortcut::ModifierGesture::Alt)
    };
    ($Key:tt) => {
        $crate::shortcut::Shortcut::Gesture($crate::__shortcut!(-> + $Key))
    };
    ($($MODIFIER:ident)|+ + $Key:tt) => {
        $crate::shortcut::Shortcut::Gesture($crate::__shortcut!(-> $($MODIFIER)|+ + $Key))
    };
    ($StarterKey:tt, $ComplementKey:tt) => {
        $crate::shortcut::Shortcut::Chord($crate::__shortcut!(=>
            + $StarterKey,
            + $ComplementKey
        ))
    };
    ($StarterKey:tt, $($COMPLEMENT_MODIFIER:ident)|+ + $ComplementKey:tt) => {
        $crate::shortcut::Shortcut::Chord($crate::__shortcut!(=>
            + $StarterKey,
            $(COMPLEMENT_MODIFIER)|* + $ComplementKey
        ))
    };
    ($($STARTER_MODIFIER:ident)|+ + $StarterKey:tt, $ComplementKey:tt) => {
        $crate::shortcut::Shortcut::Chord($crate::__shortcut!(=>
            $($STARTER_MODIFIER)|* + $StarterKey,
            + $ComplementKey
        ))
    };
    ($($STARTER_MODIFIER:ident)|+ + $StarterKey:tt, $($COMPLEMENT_MODIFIER:ident)|+ + $ComplementKey:tt) => {
        $crate::shortcut::Shortcut::Chord($crate::__shortcut!(=>
            $($STARTER_MODIFIER)|* + $StarterKey,
            $($COMPLEMENT_MODIFIER)|* + $ComplementKey
        ))
    };
}
#[doc(inline)]
pub use crate::shortcut_macro as shortcut;