Override default bindings with subset mode match

Fixes #2641.
This commit is contained in:
Christian Duerr 2019-07-15 21:32:48 +00:00 committed by GitHub
parent 1c05b3bb0c
commit 3c3239b1cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 129 additions and 14 deletions

View File

@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- On macOS, Alacritty will now fallback to Menlo if a font specified in the config cannot be loaded
- Debug ref tests are now written to disk regardless of shutdown method
- Cursor color setting with escape sequence
- Override default bindings with subset terminal mode match
## 0.3.3

View File

@ -152,17 +152,17 @@ impl<T: Eq> Binding<T> {
// the most likely item to fail so prioritizing it here allows more
// checks to be short circuited.
self.trigger == *input
&& self.mode_matches(mode)
&& self.not_mode_matches(mode)
&& mode.contains(self.mode)
&& !mode.intersects(self.notmode)
&& self.mods_match(mods, relaxed)
}
#[inline]
pub fn triggers_match(&self, binding: &Binding<T>) -> bool {
self.trigger == binding.trigger
&& self.mode == binding.mode
&& self.notmode == binding.notmode
&& self.mods == binding.mods
&& (self.mode.contains(binding.mode) || binding.mode.contains(self.mode))
&& (self.notmode.contains(binding.notmode) || binding.notmode.contains(self.notmode))
}
}
@ -173,16 +173,6 @@ impl<T> Binding<T> {
self.action.execute(ctx, mouse_mode)
}
#[inline]
fn mode_matches(&self, mode: TermMode) -> bool {
self.mode.is_empty() || mode.intersects(self.mode)
}
#[inline]
fn not_mode_matches(&self, mode: TermMode) -> bool {
self.notmode.is_empty() || !mode.intersects(self.notmode)
}
/// Check that two mods descriptions for equivalence
#[inline]
fn mods_match(&self, mods: ModifiersState, relaxed: bool) -> bool {
@ -996,6 +986,130 @@ mod tests {
const KEY: VirtualKeyCode = VirtualKeyCode::Key0;
type MockBinding = Binding<usize>;
impl Default for MockBinding {
fn default() -> Self {
Self {
mods: Default::default(),
action: Default::default(),
mode: TermMode::empty(),
notmode: TermMode::empty(),
trigger: Default::default(),
}
}
}
#[test]
fn binding_matches_itself() {
let binding = MockBinding::default();
let identical_binding = MockBinding::default();
assert!(binding.triggers_match(&identical_binding));
assert!(identical_binding.triggers_match(&binding));
}
#[test]
fn binding_matches_different_action() {
let binding = MockBinding::default();
let mut different_action = MockBinding::default();
different_action.action = Action::ClearHistory;
assert!(binding.triggers_match(&different_action));
assert!(different_action.triggers_match(&binding));
}
#[test]
fn subset_mode_binding_matches_superset() {
let mut superset_mode = MockBinding::default();
superset_mode.mode = TermMode::ALT_SCREEN | TermMode::INSERT | TermMode::ORIGIN;
let mut subset_mode = MockBinding::default();
subset_mode.mode = TermMode::ALT_SCREEN | TermMode::ORIGIN;
assert!(superset_mode.triggers_match(&subset_mode));
assert!(subset_mode.triggers_match(&superset_mode));
}
#[test]
fn subset_notmode_binding_matches_superset() {
let mut superset_notmode = MockBinding::default();
superset_notmode.notmode = TermMode::ALT_SCREEN | TermMode::INSERT | TermMode::ORIGIN;
let mut subset_notmode = MockBinding::default();
subset_notmode.notmode = TermMode::ALT_SCREEN | TermMode::ORIGIN;
assert!(superset_notmode.triggers_match(&subset_notmode));
assert!(subset_notmode.triggers_match(&superset_notmode));
}
#[test]
fn mods_binding_requires_strict_match() {
let mut superset_mods = MockBinding::default();
superset_mods.mods = ModifiersState { alt: true, logo: true, ctrl: true, shift: true };
let mut subset_mods = MockBinding::default();
subset_mods.mods = ModifiersState { alt: true, logo: false, ctrl: false, shift: false };
assert!(!superset_mods.triggers_match(&subset_mods));
assert!(!subset_mods.triggers_match(&superset_mods));
}
fn binding_trigger_input() {
let mut binding = MockBinding::default();
binding.trigger = 13;
let mods = binding.mods;
let mode = binding.mode;
assert!(binding.is_triggered_by(mode, mods, &13, true));
assert!(!binding.is_triggered_by(mode, mods, &32, true));
}
#[test]
fn binding_trigger_mods() {
let mut binding = MockBinding::default();
binding.mods = ModifiersState { alt: true, logo: true, ctrl: false, shift: false };
let superset_mods = ModifiersState { alt: true, logo: true, ctrl: true, shift: true };
let subset_mods = ModifiersState { alt: false, logo: false, ctrl: false, shift: false };
let t = binding.trigger;
let mode = binding.mode;
assert!(binding.is_triggered_by(mode, binding.mods, &t, true));
assert!(binding.is_triggered_by(mode, binding.mods, &t, false));
assert!(binding.is_triggered_by(mode, superset_mods, &t, true));
assert!(!binding.is_triggered_by(mode, superset_mods, &t, false));
assert!(!binding.is_triggered_by(mode, subset_mods, &t, true));
assert!(!binding.is_triggered_by(mode, subset_mods, &t, false));
}
#[test]
fn binding_trigger_modes() {
let mut binding = MockBinding::default();
binding.mode = TermMode::ALT_SCREEN;
let t = binding.trigger;
let mods = binding.mods;
assert!(!binding.is_triggered_by(TermMode::INSERT, mods, &t, true));
assert!(binding.is_triggered_by(TermMode::ALT_SCREEN, mods, &t, true));
assert!(binding.is_triggered_by(TermMode::ALT_SCREEN | TermMode::INSERT, mods, &t, true));
}
#[test]
fn binding_trigger_notmodes() {
let mut binding = MockBinding::default();
binding.notmode = TermMode::ALT_SCREEN;
let t = binding.trigger;
let mods = binding.mods;
assert!(binding.is_triggered_by(TermMode::INSERT, mods, &t, true));
assert!(!binding.is_triggered_by(TermMode::ALT_SCREEN, mods, &t, true));
assert!(!binding.is_triggered_by(TermMode::ALT_SCREEN | TermMode::INSERT, mods, &t, true));
}
#[derive(PartialEq)]
enum MultiClick {
DoubleClick,