#![allow(clippy::enum_glob_use)]
use std::fmt::{self, Debug, Display};
use bitflags::bitflags;
use serde::de::{self, Error as SerdeError, MapAccess, Unexpected, Visitor};
use serde::{Deserialize, Deserializer};
use std::rc::Rc;
use toml::Value as SerdeValue;
use winit::event::MouseButton;
use winit::keyboard::{
Key, KeyCode, KeyLocation as WinitKeyLocation, ModifiersState, NamedKey, PhysicalKey,
};
use winit::platform::scancode::PhysicalKeyExtScancode;
use alacritty_config_derive::{ConfigDeserialize, SerdeReplace};
use alacritty_terminal::term::TermMode;
use alacritty_terminal::vi_mode::ViMotion;
use crate::config::ui_config::{Hint, Program, StringVisitor};
/// Describes a state and action to take in that state.
///
/// This is the shared component of `MouseBinding` and `KeyBinding`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Binding<T> {
/// Modifier keys required to activate binding.
pub mods: ModifiersState,
/// String to send to PTY if mods and mode match.
pub action: Action,
/// Binding mode required to activate binding.
pub mode: BindingMode,
/// Excluded binding modes where the binding won't be activated.
pub notmode: BindingMode,
/// This property is used as part of the trigger detection code.
///
/// For example, this might be a key like "G", or a mouse button.
pub trigger: T,
}
/// Bindings that are triggered by a keyboard key.
pub type KeyBinding = Binding<BindingKey>;
/// Bindings that are triggered by a mouse button.
pub type MouseBinding = Binding<MouseButton>;
impl<T: Eq> Binding<T> {
#[inline]
pub fn is_triggered_by(&self, mode: BindingMode, mods: ModifiersState, input: &T) -> bool {
// Check input first since bindings are stored in one big list. This is
// the most likely item to fail so prioritizing it here allows more
// checks to be short circuited.
self.trigger == *input
&& self.mods == mods
&& mode.contains(self.mode)
&& !mode.intersects(self.notmode)
}
#[inline]
pub fn triggers_match(&self, binding: &Binding<T>) -> bool {
// Check the binding's key and modifiers.
if self.trigger != binding.trigger || self.mods != binding.mods {
return false;
}
let selfmode = if self.mode.is_empty() { BindingMode::all() } else { self.mode };
let bindingmode = if binding.mode.is_empty() { BindingMode::all() } else { binding.mode };
if !selfmode.intersects(bindingmode) {
return false;
}
// The bindings are never active at the same time when the required modes of one binding
// are part of the forbidden bindings of the other.
if self.mode.intersects(binding.notmode) || binding.mode.intersects(self.notmode) {
return false;
}
true
}
}
#[derive(ConfigDeserialize, Debug, Clone, PartialEq, Eq)]
pub enum Action {
/// Write an escape sequence.
#[config(skip)]
Esc(String),
/// Run given command.
#[config(skip)]
Command(Program),
/// Regex keyboard hints.
#[config(skip)]
Hint(Rc<Hint>),
/// Move vi mode cursor.
#[config(skip)]
ViMotion(ViMotion),
/// Perform vi mode action.
#[config(skip)]
Vi(ViAction),
/// Perform search mode action.
#[config(skip)]
|