Allow mode-exclusive bindings in any mode

This patch enables binding chains that go beyond mode changes by
allowing bindings to be defined for modes they do not usually have an
effect in.

Fixes #4073.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9609ecc..8288b58 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@
 
 ## 0.13.0-dev
 
+### Changed
+
+- Mode-specific bindings can now be bound in any mode for easier macros
+
 ### Fixed
 
 - Character `;` inside the `URI` in `OSC 8` sequence breaking the URI
diff --git a/alacritty/src/config/bindings.rs b/alacritty/src/config/bindings.rs
index e3ee41e..0e50807 100644
--- a/alacritty/src/config/bindings.rs
+++ b/alacritty/src/config/bindings.rs
@@ -1114,26 +1114,8 @@
 
                 let action = match (action, chars, command) {
                     (Some(action @ Action::ViMotion(_)), None, None)
-                    | (Some(action @ Action::Vi(_)), None, None) => {
-                        if !mode.intersects(BindingMode::VI) || not_mode.intersects(BindingMode::VI)
-                        {
-                            return Err(V::Error::custom(format!(
-                                "action `{}` is only available in vi mode, try adding `mode: Vi`",
-                                action,
-                            )));
-                        }
-                        action
-                    },
-                    (Some(action @ Action::Search(_)), None, None) => {
-                        if !mode.intersects(BindingMode::SEARCH) {
-                            return Err(V::Error::custom(format!(
-                                "action `{}` is only available in search mode, try adding `mode: \
-                                 Search`",
-                                action,
-                            )));
-                        }
-                        action
-                    },
+                    | (Some(action @ Action::Vi(_)), None, None) => action,
+                    (Some(action @ Action::Search(_)), None, None) => action,
                     (Some(action @ Action::Mouse(_)), None, None) => {
                         if mouse.is_none() {
                             return Err(V::Error::custom(format!(
diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs
index 3248a1e..21b866d 100644
--- a/alacritty/src/event.rs
+++ b/alacritty/src/event.rs
@@ -496,6 +496,7 @@
         // Enable IME so we can input into the search bar with it if we were in Vi mode.
         self.window().set_ime_allowed(true);
 
+        self.terminal.mark_fully_damaged();
         self.display.pending_update.dirty = true;
     }
 
@@ -983,6 +984,7 @@
         let vi_mode = self.terminal.mode().contains(TermMode::VI);
         self.window().set_ime_allowed(!vi_mode);
 
+        self.terminal.mark_fully_damaged();
         self.display.pending_update.dirty = true;
         self.search_state.history_index = None;
 
diff --git a/alacritty/src/input.rs b/alacritty/src/input.rs
index 992b01d..935fc04 100644
--- a/alacritty/src/input.rs
+++ b/alacritty/src/input.rs
@@ -14,6 +14,7 @@
 use std::mem;
 use std::time::{Duration, Instant};
 
+use log::debug;
 use winit::dpi::PhysicalPosition;
 use winit::event::{
     ElementState, KeyboardInput, ModifiersState, MouseButton, MouseScrollDelta,
@@ -162,6 +163,11 @@
                 ctx.on_typing_start();
                 ctx.toggle_vi_mode()
             },
+            action @ (Action::ViMotion(_) | Action::Vi(_))
+                if !ctx.terminal().mode().contains(TermMode::VI) =>
+            {
+                debug!("Ignoring {action:?}: Vi mode inactive");
+            },
             Action::ViMotion(motion) => {
                 ctx.on_typing_start();
                 ctx.terminal_mut().vi_motion(*motion);
@@ -246,6 +252,9 @@
 
                 ctx.scroll(Scroll::Delta(scroll_lines));
             },
+            action @ Action::Search(_) if !ctx.search_active() => {
+                debug!("Ignoring {action:?}: Search mode inactive");
+            },
             Action::Search(SearchAction::SearchFocusNext) => {
                 ctx.advance_search_origin(ctx.search_direction());
             },