From 3c3239b1cb4b21ee1dec8ccfbc5ec957b6e66798 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Mon, 15 Jul 2019 21:32:48 +0000 Subject: [PATCH] Override default bindings with subset mode match Fixes #2641. --- CHANGELOG.md | 1 + alacritty_terminal/src/input.rs | 142 ++++++++++++++++++++++++++++---- 2 files changed, 129 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ede22e9..ac9d857 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/alacritty_terminal/src/input.rs b/alacritty_terminal/src/input.rs index f4e38fa..83ab026 100644 --- a/alacritty_terminal/src/input.rs +++ b/alacritty_terminal/src/input.rs @@ -152,17 +152,17 @@ impl Binding { // 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) -> 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 Binding { 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; + + 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,