From 8a7f8c9d3eca214578439da417d37395971a711d Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Sat, 19 Dec 2020 04:07:20 +0000 Subject: [PATCH] Add separate mode for search bindings --- CHANGELOG.md | 1 + alacritty.yml | 193 +++++----- alacritty/src/config/bindings.rs | 614 ++++++++++++++++++++----------- alacritty/src/config/mod.rs | 2 +- alacritty/src/event.rs | 28 +- alacritty/src/input.rs | 166 ++++----- 6 files changed, 584 insertions(+), 420 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7331f435..0175d10f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Support for cursor blinking escapes (`CSI ? 12 h`, `CSI ? 12 l` and `CSI Ps SP q`) - IME support on Windows - Urgency support on Windows +- Customizable keybindings for search ### Changed diff --git a/alacritty.yml b/alacritty.yml index 2f2e5dc9..7e36928b 100644 --- a/alacritty.yml +++ b/alacritty.yml @@ -611,6 +611,18 @@ # - SearchEnd # End of the match to the right of the vi mode cursor. # +# - Search mode exclusive actions: +# - SearchFocusNext +# Move the focus to the next search match. +# - SearchFocusPrevious +# Move the focus to the previous search match. +# - SearchConfirm +# - SearchCancel +# - SearchClear +# Reset the search regex. +# - SearchDeleteWord +# Delete the last word in the search regex. +# # - macOS exclusive actions: # - ToggleSimpleFullscreen # Enter fullscreen without occupying another space. @@ -650,6 +662,7 @@ # # - AppCursor # - AppKeypad +# - Search # - Alt # - Vi # @@ -664,101 +677,109 @@ # If the same trigger is assigned to multiple actions, all of them are executed # in the order they were defined in. #key_bindings: - #- { key: Paste, action: Paste } - #- { key: Copy, action: Copy } - #- { key: L, mods: Control, action: ClearLogNotice } - #- { key: L, mods: Control, mode: ~Vi, chars: "\x0c" } - #- { key: PageUp, mods: Shift, mode: ~Alt, action: ScrollPageUp, } - #- { key: PageDown, mods: Shift, mode: ~Alt, action: ScrollPageDown } - #- { key: Home, mods: Shift, mode: ~Alt, action: ScrollToTop, } - #- { key: End, mods: Shift, mode: ~Alt, action: ScrollToBottom } + #- { key: Paste, action: Paste } + #- { key: Copy, action: Copy } + #- { key: L, mods: Control, action: ClearLogNotice } + #- { key: L, mods: Control, mode: ~Vi|~Search, chars: "\x0c" } + #- { key: PageUp, mods: Shift, mode: ~Alt, action: ScrollPageUp, } + #- { key: PageDown, mods: Shift, mode: ~Alt, action: ScrollPageDown } + #- { key: Home, mods: Shift, mode: ~Alt, action: ScrollToTop, } + #- { key: End, mods: Shift, mode: ~Alt, action: ScrollToBottom } # Vi Mode - #- { key: Space, mods: Shift|Control, mode: Vi, action: ScrollToBottom } - #- { key: Space, mods: Shift|Control, action: ToggleViMode } - #- { key: Escape, mode: Vi, action: ClearSelection } - #- { key: I, mode: Vi, action: ScrollToBottom } - #- { key: I, mode: Vi, action: ToggleViMode } - #- { key: C, mods: Control, mode: Vi, action: ToggleViMode } - #- { key: Y, mods: Control, mode: Vi, action: ScrollLineUp } - #- { key: E, mods: Control, mode: Vi, action: ScrollLineDown } - #- { key: G, mode: Vi, action: ScrollToTop } - #- { key: G, mods: Shift, mode: Vi, action: ScrollToBottom } - #- { key: B, mods: Control, mode: Vi, action: ScrollPageUp } - #- { key: F, mods: Control, mode: Vi, action: ScrollPageDown } - #- { key: U, mods: Control, mode: Vi, action: ScrollHalfPageUp } - #- { key: D, mods: Control, mode: Vi, action: ScrollHalfPageDown } - #- { key: Y, mode: Vi, action: Copy } - #- { key: Y, mode: Vi, action: ClearSelection } - #- { key: Copy, mode: Vi, action: ClearSelection } - #- { key: V, mode: Vi, action: ToggleNormalSelection } - #- { key: V, mods: Shift, mode: Vi, action: ToggleLineSelection } - #- { key: V, mods: Control, mode: Vi, action: ToggleBlockSelection } - #- { key: V, mods: Alt, mode: Vi, action: ToggleSemanticSelection } - #- { key: Return, mode: Vi, action: Open } - #- { key: K, mode: Vi, action: Up } - #- { key: J, mode: Vi, action: Down } - #- { key: H, mode: Vi, action: Left } - #- { key: L, mode: Vi, action: Right } - #- { key: Up, mode: Vi, action: Up } - #- { key: Down, mode: Vi, action: Down } - #- { key: Left, mode: Vi, action: Left } - #- { key: Right, mode: Vi, action: Right } - #- { key: Key0, mode: Vi, action: First } - #- { key: Key4, mods: Shift, mode: Vi, action: Last } - #- { key: Key6, mods: Shift, mode: Vi, action: FirstOccupied } - #- { key: H, mods: Shift, mode: Vi, action: High } - #- { key: M, mods: Shift, mode: Vi, action: Middle } - #- { key: L, mods: Shift, mode: Vi, action: Low } - #- { key: B, mode: Vi, action: SemanticLeft } - #- { key: W, mode: Vi, action: SemanticRight } - #- { key: E, mode: Vi, action: SemanticRightEnd } - #- { key: B, mods: Shift, mode: Vi, action: WordLeft } - #- { key: W, mods: Shift, mode: Vi, action: WordRight } - #- { key: E, mods: Shift, mode: Vi, action: WordRightEnd } - #- { key: Key5, mods: Shift, mode: Vi, action: Bracket } - #- { key: Slash, mode: Vi, action: SearchForward } - #- { key: Slash, mods: Shift, mode: Vi, action: SearchBackward } - #- { key: N, mode: Vi, action: SearchNext } - #- { key: N, mods: Shift, mode: Vi, action: SearchPrevious } + #- { key: Space, mods: Shift|Control, mode: Vi|~Search, action: ScrollToBottom } + #- { key: Space, mods: Shift|Control, mode: ~Search, action: ToggleViMode } + #- { key: Escape, mode: Vi|~Search, action: ClearSelection } + #- { key: I, mode: Vi|~Search, action: ScrollToBottom } + #- { key: I, mode: Vi|~Search, action: ToggleViMode } + #- { key: C, mods: Control, mode: Vi|~Search, action: ToggleViMode } + #- { key: Y, mods: Control, mode: Vi|~Search, action: ScrollLineUp } + #- { key: E, mods: Control, mode: Vi|~Search, action: ScrollLineDown } + #- { key: G, mode: Vi|~Search, action: ScrollToTop } + #- { key: G, mods: Shift, mode: Vi|~Search, action: ScrollToBottom } + #- { key: B, mods: Control, mode: Vi|~Search, action: ScrollPageUp } + #- { key: F, mods: Control, mode: Vi|~Search, action: ScrollPageDown } + #- { key: U, mods: Control, mode: Vi|~Search, action: ScrollHalfPageUp } + #- { key: D, mods: Control, mode: Vi|~Search, action: ScrollHalfPageDown } + #- { key: Y, mode: Vi|~Search, action: Copy } + #- { key: Y, mode: Vi|~Search, action: ClearSelection } + #- { key: Copy, mode: Vi|~Search, action: ClearSelection } + #- { key: V, mode: Vi|~Search, action: ToggleNormalSelection } + #- { key: V, mods: Shift, mode: Vi|~Search, action: ToggleLineSelection } + #- { key: V, mods: Control, mode: Vi|~Search, action: ToggleBlockSelection } + #- { key: V, mods: Alt, mode: Vi|~Search, action: ToggleSemanticSelection } + #- { key: Return, mode: Vi|~Search, action: Open } + #- { key: K, mode: Vi|~Search, action: Up } + #- { key: J, mode: Vi|~Search, action: Down } + #- { key: H, mode: Vi|~Search, action: Left } + #- { key: L, mode: Vi|~Search, action: Right } + #- { key: Up, mode: Vi|~Search, action: Up } + #- { key: Down, mode: Vi|~Search, action: Down } + #- { key: Left, mode: Vi|~Search, action: Left } + #- { key: Right, mode: Vi|~Search, action: Right } + #- { key: Key0, mode: Vi|~Search, action: First } + #- { key: Key4, mods: Shift, mode: Vi|~Search, action: Last } + #- { key: Key6, mods: Shift, mode: Vi|~Search, action: FirstOccupied } + #- { key: H, mods: Shift, mode: Vi|~Search, action: High } + #- { key: M, mods: Shift, mode: Vi|~Search, action: Middle } + #- { key: L, mods: Shift, mode: Vi|~Search, action: Low } + #- { key: B, mode: Vi|~Search, action: SemanticLeft } + #- { key: W, mode: Vi|~Search, action: SemanticRight } + #- { key: E, mode: Vi|~Search, action: SemanticRightEnd } + #- { key: B, mods: Shift, mode: Vi|~Search, action: WordLeft } + #- { key: W, mods: Shift, mode: Vi|~Search, action: WordRight } + #- { key: E, mods: Shift, mode: Vi|~Search, action: WordRightEnd } + #- { key: Key5, mods: Shift, mode: Vi|~Search, action: Bracket } + #- { key: Slash, mode: Vi|~Search, action: SearchForward } + #- { key: Slash, mods: Shift, mode: Vi|~Search, action: SearchBackward } + #- { key: N, mode: Vi|~Search, action: SearchNext } + #- { key: N, mods: Shift, mode: Vi|~Search, action: SearchPrevious } + + # Search Mode + #- { key: Return, mode: Search|Vi, action: SearchConfirm } + #- { key: Escape, mode: Search, action: SearchCancel } + #- { key: U, mods: Control, mode: Search, action: SearchClear } + #- { key: W, mods: Control, mode: Search, action: SearchDeleteWord } + #- { key: Return, mode: Search|~Vi, action: SearchFocusNext } + #- { key: Return, mods: Shift, mode: Search|~Vi, action: SearchFocusPrevious } # (Windows, Linux, and BSD only) - #- { key: V, mods: Control|Shift, action: Paste } - #- { key: C, mods: Control|Shift, action: Copy } - #- { key: F, mods: Control|Shift, action: SearchForward } - #- { key: B, mods: Control|Shift, action: SearchBackward } - #- { key: C, mods: Control|Shift, mode: Vi, action: ClearSelection } - #- { key: Insert, mods: Shift, action: PasteSelection } - #- { key: Key0, mods: Control, action: ResetFontSize } - #- { key: Equals, mods: Control, action: IncreaseFontSize } - #- { key: Plus, mods: Control, action: IncreaseFontSize } - #- { key: NumpadAdd, mods: Control, action: IncreaseFontSize } - #- { key: Minus, mods: Control, action: DecreaseFontSize } - #- { key: NumpadSubtract, mods: Control, action: DecreaseFontSize } + #- { key: V, mods: Control|Shift, mode: ~Vi, action: Paste } + #- { key: C, mods: Control|Shift, action: Copy } + #- { key: F, mods: Control|Shift, mode: ~Search, action: SearchForward } + #- { key: B, mods: Control|Shift, mode: ~Search, action: SearchBackward } + #- { key: C, mods: Control|Shift, mode: Vi|~Search, action: ClearSelection } + #- { key: Insert, mods: Shift, action: PasteSelection } + #- { key: Key0, mods: Control, action: ResetFontSize } + #- { key: Equals, mods: Control, action: IncreaseFontSize } + #- { key: Plus, mods: Control, action: IncreaseFontSize } + #- { key: NumpadAdd, mods: Control, action: IncreaseFontSize } + #- { key: Minus, mods: Control, action: DecreaseFontSize } + #- { key: NumpadSubtract, mods: Control, action: DecreaseFontSize } # (Windows only) #- { key: Return, mods: Alt, action: ToggleFullscreen } # (macOS only) - #- { key: K, mods: Command, mode: ~Vi, chars: "\x0c" } - #- { key: Key0, mods: Command, action: ResetFontSize } - #- { key: Equals, mods: Command, action: IncreaseFontSize } - #- { key: Plus, mods: Command, action: IncreaseFontSize } - #- { key: NumpadAdd, mods: Command, action: IncreaseFontSize } - #- { key: Minus, mods: Command, action: DecreaseFontSize } - #- { key: NumpadSubtract, mods: Command, action: DecreaseFontSize } - #- { key: K, mods: Command, action: ClearHistory } - #- { key: V, mods: Command, action: Paste } - #- { key: C, mods: Command, action: Copy } - #- { key: C, mods: Command, mode: Vi, action: ClearSelection } - #- { key: H, mods: Command, action: Hide } - #- { key: M, mods: Command, action: Minimize } - #- { key: Q, mods: Command, action: Quit } - #- { key: W, mods: Command, action: Quit } - #- { key: N, mods: Command, action: SpawnNewInstance } - #- { key: F, mods: Command|Control, action: ToggleFullscreen } - #- { key: F, mods: Command, action: SearchForward } - #- { key: B, mods: Command, action: SearchBackward } + #- { key: K, mods: Command, mode: ~Vi|~Search, chars: "\x0c" } + #- { key: K, mods: Command, mode: ~Vi|~Search, action: ClearHistory } + #- { key: Key0, mods: Command, action: ResetFontSize } + #- { key: Equals, mods: Command, action: IncreaseFontSize } + #- { key: Plus, mods: Command, action: IncreaseFontSize } + #- { key: NumpadAdd, mods: Command, action: IncreaseFontSize } + #- { key: Minus, mods: Command, action: DecreaseFontSize } + #- { key: NumpadSubtract, mods: Command, action: DecreaseFontSize } + #- { key: V, mods: Command, action: Paste } + #- { key: C, mods: Command, action: Copy } + #- { key: C, mods: Command, mode: Vi|~Search, action: ClearSelection } + #- { key: H, mods: Command, action: Hide } + #- { key: M, mods: Command, action: Minimize } + #- { key: Q, mods: Command, action: Quit } + #- { key: W, mods: Command, action: Quit } + #- { key: N, mods: Command, action: SpawnNewInstance } + #- { key: F, mods: Command|Control, action: ToggleFullscreen } + #- { key: F, mods: Command, mode: ~Search, action: SearchForward } + #- { key: B, mods: Command, mode: ~Search, action: SearchBackward } #debug: # Display the time it takes to redraw each frame. diff --git a/alacritty/src/config/bindings.rs b/alacritty/src/config/bindings.rs index f5b63561..1dac8cdd 100644 --- a/alacritty/src/config/bindings.rs +++ b/alacritty/src/config/bindings.rs @@ -2,6 +2,7 @@ use std::fmt::{self, Debug, Display}; +use bitflags::bitflags; use glutin::event::VirtualKeyCode::*; use glutin::event::{ModifiersState, MouseButton, VirtualKeyCode}; use serde::de::Error as SerdeError; @@ -24,11 +25,11 @@ pub struct Binding { /// String to send to PTY if mods and mode match. pub action: Action, - /// Terminal mode required to activate binding. - pub mode: TermMode, + /// Binding mode required to activate binding. + pub mode: BindingMode, - /// excluded terminal modes where the binding won't be activated. - pub notmode: TermMode, + /// Excluded binding modes where the binding won't be activated. + pub notmode: BindingMode, /// This property is used as part of the trigger detection code. /// @@ -44,7 +45,7 @@ pub type MouseBinding = Binding; impl Binding { #[inline] - pub fn is_triggered_by(&self, mode: TermMode, mods: ModifiersState, input: &T) -> bool { + 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. @@ -61,8 +62,8 @@ impl Binding { return false; } - let selfmode = if self.mode.is_empty() { TermMode::ANY } else { self.mode }; - let bindingmode = if binding.mode.is_empty() { TermMode::ANY } else { binding.mode }; + 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; @@ -96,6 +97,10 @@ pub enum Action { #[serde(skip)] ViAction(ViAction), + /// Perform search mode action. + #[serde(skip)] + SearchAction(SearchAction), + /// Paste contents of system clipboard. Paste, @@ -192,6 +197,24 @@ impl From<&'static str> for Action { } } +impl From for Action { + fn from(action: ViAction) -> Self { + Self::ViAction(action) + } +} + +impl From for Action { + fn from(motion: ViMotion) -> Self { + Self::ViMotion(motion) + } +} + +impl From for Action { + fn from(action: SearchAction) -> Self { + Self::SearchAction(action) + } +} + /// Display trait used for error logging. impl Display for Action { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -226,16 +249,21 @@ pub enum ViAction { Open, } -impl From for Action { - fn from(action: ViAction) -> Self { - Self::ViAction(action) - } -} - -impl From for Action { - fn from(motion: ViMotion) -> Self { - Self::ViMotion(motion) - } +/// Search mode specific actions. +#[derive(Deserialize, Debug, Copy, Clone, PartialEq, Eq)] +pub enum SearchAction { + /// Move the focus to the next search match. + SearchFocusNext, + /// Move the focus to the previous search match. + SearchFocusPrevious, + /// Confirm the active search. + SearchConfirm, + /// Cancel the active search. + SearchCancel, + /// Reset the search regex. + SearchClear, + /// Delete the last word in the search regex. + SearchDeleteWord, } macro_rules! bindings { @@ -277,9 +305,9 @@ macro_rules! bindings { $( let mut _mods = ModifiersState::empty(); $(_mods = $mods;)* - let mut _mode = TermMode::empty(); + let mut _mode = BindingMode::empty(); $(_mode.insert($mode);)* - let mut _notmode = TermMode::empty(); + let mut _notmode = BindingMode::empty(); $(_notmode.insert($notmode);)* v.push($ty { @@ -298,7 +326,7 @@ macro_rules! bindings { pub fn default_mouse_bindings() -> Vec { bindings!( MouseBinding; - MouseButton::Middle, ~TermMode::VI; Action::PasteSelection; + MouseButton::Middle, ~BindingMode::VI; Action::PasteSelection; ) } @@ -306,109 +334,179 @@ pub fn default_key_bindings() -> Vec { let mut bindings = bindings!( KeyBinding; Copy; Action::Copy; - Copy, +TermMode::VI; Action::ClearSelection; - Paste, ~TermMode::VI; Action::Paste; + Copy, +BindingMode::VI; Action::ClearSelection; + Paste, ~BindingMode::VI; Action::Paste; L, ModifiersState::CTRL; Action::ClearLogNotice; - L, ModifiersState::CTRL, ~TermMode::VI; Action::Esc("\x0c".into()); - Tab, ModifiersState::SHIFT, ~TermMode::VI; Action::Esc("\x1b[Z".into()); - Back, ModifiersState::ALT, ~TermMode::VI; Action::Esc("\x1b\x7f".into()); - Back, ModifiersState::SHIFT, ~TermMode::VI; Action::Esc("\x7f".into()); - Home, ModifiersState::SHIFT, ~TermMode::ALT_SCREEN; Action::ScrollToTop; - End, ModifiersState::SHIFT, ~TermMode::ALT_SCREEN; Action::ScrollToBottom; - PageUp, ModifiersState::SHIFT, ~TermMode::ALT_SCREEN; Action::ScrollPageUp; - PageDown, ModifiersState::SHIFT, ~TermMode::ALT_SCREEN; Action::ScrollPageDown; - Home, ModifiersState::SHIFT, +TermMode::ALT_SCREEN, ~TermMode::VI; - Action::Esc("\x1b[1;2H".into()); - End, ModifiersState::SHIFT, +TermMode::ALT_SCREEN, ~TermMode::VI; - Action::Esc("\x1b[1;2F".into()); - PageUp, ModifiersState::SHIFT, +TermMode::ALT_SCREEN, ~TermMode::VI; - Action::Esc("\x1b[5;2~".into()); - PageDown, ModifiersState::SHIFT, +TermMode::ALT_SCREEN, ~TermMode::VI; - Action::Esc("\x1b[6;2~".into()); - Home, +TermMode::APP_CURSOR, ~TermMode::VI; Action::Esc("\x1bOH".into()); - Home, ~TermMode::APP_CURSOR, ~TermMode::VI; Action::Esc("\x1b[H".into()); - End, +TermMode::APP_CURSOR, ~TermMode::VI; Action::Esc("\x1bOF".into()); - End, ~TermMode::APP_CURSOR, ~TermMode::VI; Action::Esc("\x1b[F".into()); - Up, +TermMode::APP_CURSOR, ~TermMode::VI; Action::Esc("\x1bOA".into()); - Up, ~TermMode::APP_CURSOR, ~TermMode::VI; Action::Esc("\x1b[A".into()); - Down, +TermMode::APP_CURSOR, ~TermMode::VI; Action::Esc("\x1bOB".into()); - Down, ~TermMode::APP_CURSOR, ~TermMode::VI; Action::Esc("\x1b[B".into()); - Right, +TermMode::APP_CURSOR, ~TermMode::VI; Action::Esc("\x1bOC".into()); - Right, ~TermMode::APP_CURSOR, ~TermMode::VI; Action::Esc("\x1b[C".into()); - Left, +TermMode::APP_CURSOR, ~TermMode::VI; Action::Esc("\x1bOD".into()); - Left, ~TermMode::APP_CURSOR, ~TermMode::VI; Action::Esc("\x1b[D".into()); - Back, ~TermMode::VI; Action::Esc("\x7f".into()); - Insert, ~TermMode::VI; Action::Esc("\x1b[2~".into()); - Delete, ~TermMode::VI; Action::Esc("\x1b[3~".into()); - PageUp, ~TermMode::VI; Action::Esc("\x1b[5~".into()); - PageDown, ~TermMode::VI; Action::Esc("\x1b[6~".into()); - F1, ~TermMode::VI; Action::Esc("\x1bOP".into()); - F2, ~TermMode::VI; Action::Esc("\x1bOQ".into()); - F3, ~TermMode::VI; Action::Esc("\x1bOR".into()); - F4, ~TermMode::VI; Action::Esc("\x1bOS".into()); - F5, ~TermMode::VI; Action::Esc("\x1b[15~".into()); - F6, ~TermMode::VI; Action::Esc("\x1b[17~".into()); - F7, ~TermMode::VI; Action::Esc("\x1b[18~".into()); - F8, ~TermMode::VI; Action::Esc("\x1b[19~".into()); - F9, ~TermMode::VI; Action::Esc("\x1b[20~".into()); - F10, ~TermMode::VI; Action::Esc("\x1b[21~".into()); - F11, ~TermMode::VI; Action::Esc("\x1b[23~".into()); - F12, ~TermMode::VI; Action::Esc("\x1b[24~".into()); - F13, ~TermMode::VI; Action::Esc("\x1b[25~".into()); - F14, ~TermMode::VI; Action::Esc("\x1b[26~".into()); - F15, ~TermMode::VI; Action::Esc("\x1b[28~".into()); - F16, ~TermMode::VI; Action::Esc("\x1b[29~".into()); - F17, ~TermMode::VI; Action::Esc("\x1b[31~".into()); - F18, ~TermMode::VI; Action::Esc("\x1b[32~".into()); - F19, ~TermMode::VI; Action::Esc("\x1b[33~".into()); - F20, ~TermMode::VI; Action::Esc("\x1b[34~".into()); - NumpadEnter, ~TermMode::VI; Action::Esc("\n".into()); - Space, ModifiersState::SHIFT | ModifiersState::CTRL, +TermMode::VI; Action::ScrollToBottom; - Space, ModifiersState::SHIFT | ModifiersState::CTRL; Action::ToggleViMode; - Escape, +TermMode::VI; Action::ClearSelection; - I, +TermMode::VI; Action::ScrollToBottom; - I, +TermMode::VI; Action::ToggleViMode; - C, ModifiersState::CTRL, +TermMode::VI; Action::ToggleViMode; - Y, ModifiersState::CTRL, +TermMode::VI; Action::ScrollLineUp; - E, ModifiersState::CTRL, +TermMode::VI; Action::ScrollLineDown; - G, +TermMode::VI; Action::ScrollToTop; - G, ModifiersState::SHIFT, +TermMode::VI; Action::ScrollToBottom; - B, ModifiersState::CTRL, +TermMode::VI; Action::ScrollPageUp; - F, ModifiersState::CTRL, +TermMode::VI; Action::ScrollPageDown; - U, ModifiersState::CTRL, +TermMode::VI; Action::ScrollHalfPageUp; - D, ModifiersState::CTRL, +TermMode::VI; Action::ScrollHalfPageDown; - Y, +TermMode::VI; Action::Copy; - Y, +TermMode::VI; Action::ClearSelection; - Slash, +TermMode::VI; Action::SearchForward; - Slash, ModifiersState::SHIFT, +TermMode::VI; Action::SearchBackward; - V, +TermMode::VI; ViAction::ToggleNormalSelection; - V, ModifiersState::SHIFT, +TermMode::VI; ViAction::ToggleLineSelection; - V, ModifiersState::CTRL, +TermMode::VI; ViAction::ToggleBlockSelection; - V, ModifiersState::ALT, +TermMode::VI; ViAction::ToggleSemanticSelection; - N, +TermMode::VI; ViAction::SearchNext; - N, ModifiersState::SHIFT, +TermMode::VI; ViAction::SearchPrevious; - Return, +TermMode::VI; ViAction::Open; - K, +TermMode::VI; ViMotion::Up; - J, +TermMode::VI; ViMotion::Down; - H, +TermMode::VI; ViMotion::Left; - L, +TermMode::VI; ViMotion::Right; - Up, +TermMode::VI; ViMotion::Up; - Down, +TermMode::VI; ViMotion::Down; - Left, +TermMode::VI; ViMotion::Left; - Right, +TermMode::VI; ViMotion::Right; - Key0, +TermMode::VI; ViMotion::First; - Key4, ModifiersState::SHIFT, +TermMode::VI; ViMotion::Last; - Key6, ModifiersState::SHIFT, +TermMode::VI; ViMotion::FirstOccupied; - H, ModifiersState::SHIFT, +TermMode::VI; ViMotion::High; - M, ModifiersState::SHIFT, +TermMode::VI; ViMotion::Middle; - L, ModifiersState::SHIFT, +TermMode::VI; ViMotion::Low; - B, +TermMode::VI; ViMotion::SemanticLeft; - W, +TermMode::VI; ViMotion::SemanticRight; - E, +TermMode::VI; ViMotion::SemanticRightEnd; - B, ModifiersState::SHIFT, +TermMode::VI; ViMotion::WordLeft; - W, ModifiersState::SHIFT, +TermMode::VI; ViMotion::WordRight; - E, ModifiersState::SHIFT, +TermMode::VI; ViMotion::WordRightEnd; - Key5, ModifiersState::SHIFT, +TermMode::VI; ViMotion::Bracket; + L, ModifiersState::CTRL, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x0c".into()); + Tab, ModifiersState::SHIFT, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x1b[Z".into()); + Back, ModifiersState::ALT, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x1b\x7f".into()); + Back, ModifiersState::SHIFT, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x7f".into()); + Home, ModifiersState::SHIFT, ~BindingMode::ALT_SCREEN; Action::ScrollToTop; + End, ModifiersState::SHIFT, ~BindingMode::ALT_SCREEN; Action::ScrollToBottom; + PageUp, ModifiersState::SHIFT, ~BindingMode::ALT_SCREEN; Action::ScrollPageUp; + PageDown, ModifiersState::SHIFT, ~BindingMode::ALT_SCREEN; Action::ScrollPageDown; + Home, ModifiersState::SHIFT, +BindingMode::ALT_SCREEN, + ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[1;2H".into()); + End, ModifiersState::SHIFT, +BindingMode::ALT_SCREEN, + ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[1;2F".into()); + PageUp, ModifiersState::SHIFT, +BindingMode::ALT_SCREEN, + ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[5;2~".into()); + PageDown, ModifiersState::SHIFT, +BindingMode::ALT_SCREEN, + ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[6;2~".into()); + Home, +BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x1bOH".into()); + Home, ~BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x1b[H".into()); + End, +BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x1bOF".into()); + End, ~BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x1b[F".into()); + Up, +BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x1bOA".into()); + Up, ~BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x1b[A".into()); + Down, +BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x1bOB".into()); + Down, ~BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x1b[B".into()); + Right, +BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x1bOC".into()); + Right, ~BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x1b[C".into()); + Left, +BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x1bOD".into()); + Left, ~BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x1b[D".into()); + Back, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x7f".into()); + Insert, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[2~".into()); + Delete, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[3~".into()); + PageUp, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[5~".into()); + PageDown, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[6~".into()); + F1, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1bOP".into()); + F2, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1bOQ".into()); + F3, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1bOR".into()); + F4, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1bOS".into()); + F5, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[15~".into()); + F6, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[17~".into()); + F7, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[18~".into()); + F8, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[19~".into()); + F9, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[20~".into()); + F10, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[21~".into()); + F11, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[23~".into()); + F12, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[24~".into()); + F13, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[25~".into()); + F14, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[26~".into()); + F15, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[28~".into()); + F16, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[29~".into()); + F17, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[31~".into()); + F18, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[32~".into()); + F19, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[33~".into()); + F20, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[34~".into()); + NumpadEnter, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\n".into()); + Space, ModifiersState::SHIFT | ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH; + Action::ScrollToBottom; + Space, ModifiersState::SHIFT | ModifiersState::CTRL, ~BindingMode::SEARCH; + Action::ToggleViMode; + Escape, +BindingMode::VI, ~BindingMode::SEARCH; + Action::ClearSelection; + I, +BindingMode::VI, ~BindingMode::SEARCH; + Action::ScrollToBottom; + I, +BindingMode::VI, ~BindingMode::SEARCH; + Action::ToggleViMode; + C, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH; + Action::ToggleViMode; + Y, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH; + Action::ScrollLineUp; + E, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH; + Action::ScrollLineDown; + G, +BindingMode::VI, ~BindingMode::SEARCH; + Action::ScrollToTop; + G, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH; + Action::ScrollToBottom; + B, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH; + Action::ScrollPageUp; + F, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH; + Action::ScrollPageDown; + U, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH; + Action::ScrollHalfPageUp; + D, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH; + Action::ScrollHalfPageDown; + Y, +BindingMode::VI, ~BindingMode::SEARCH; Action::Copy; + Y, +BindingMode::VI, ~BindingMode::SEARCH; + Action::ClearSelection; + Slash, +BindingMode::VI, ~BindingMode::SEARCH; + Action::SearchForward; + Slash, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH; + Action::SearchBackward; + V, +BindingMode::VI, ~BindingMode::SEARCH; + ViAction::ToggleNormalSelection; + V, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH; + ViAction::ToggleLineSelection; + V, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH; + ViAction::ToggleBlockSelection; + V, ModifiersState::ALT, +BindingMode::VI, ~BindingMode::SEARCH; + ViAction::ToggleSemanticSelection; + N, +BindingMode::VI, ~BindingMode::SEARCH; + ViAction::SearchNext; + N, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH; + ViAction::SearchPrevious; + Return, +BindingMode::VI, ~BindingMode::SEARCH; + ViAction::Open; + K, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::Up; + J, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::Down; + H, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::Left; + L, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::Right; + Up, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::Up; + Down, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::Down; + Left, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::Left; + Right, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::Right; + Key0, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::First; + Key4, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::Last; + Key6, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::FirstOccupied; + H, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::High; + M, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::Middle; + L, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::Low; + B, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::SemanticLeft; + W, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::SemanticRight; + E, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::SemanticRightEnd; + B, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::WordLeft; + W, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::WordRight; + E, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::WordRightEnd; + Key5, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH; + ViMotion::Bracket; + Return, +BindingMode::SEARCH, +BindingMode::VI; + SearchAction::SearchConfirm; + Escape, +BindingMode::SEARCH; SearchAction::SearchCancel; + U, ModifiersState::CTRL, +BindingMode::SEARCH; SearchAction::SearchClear; + W, ModifiersState::CTRL, +BindingMode::SEARCH; SearchAction::SearchDeleteWord; + Return, +BindingMode::SEARCH, ~BindingMode::VI; + SearchAction::SearchFocusNext; + Return, ModifiersState::SHIFT, +BindingMode::SEARCH, ~BindingMode::VI; + SearchAction::SearchFocusPrevious; ); // Code Modifiers @@ -437,42 +535,72 @@ pub fn default_key_bindings() -> Vec { let modifiers_code = index + 2; bindings.extend(bindings!( KeyBinding; - Delete, mods, ~TermMode::VI; Action::Esc(format!("\x1b[3;{}~", modifiers_code)); - Up, mods, ~TermMode::VI; Action::Esc(format!("\x1b[1;{}A", modifiers_code)); - Down, mods, ~TermMode::VI; Action::Esc(format!("\x1b[1;{}B", modifiers_code)); - Right, mods, ~TermMode::VI; Action::Esc(format!("\x1b[1;{}C", modifiers_code)); - Left, mods, ~TermMode::VI; Action::Esc(format!("\x1b[1;{}D", modifiers_code)); - F1, mods, ~TermMode::VI; Action::Esc(format!("\x1b[1;{}P", modifiers_code)); - F2, mods, ~TermMode::VI; Action::Esc(format!("\x1b[1;{}Q", modifiers_code)); - F3, mods, ~TermMode::VI; Action::Esc(format!("\x1b[1;{}R", modifiers_code)); - F4, mods, ~TermMode::VI; Action::Esc(format!("\x1b[1;{}S", modifiers_code)); - F5, mods, ~TermMode::VI; Action::Esc(format!("\x1b[15;{}~", modifiers_code)); - F6, mods, ~TermMode::VI; Action::Esc(format!("\x1b[17;{}~", modifiers_code)); - F7, mods, ~TermMode::VI; Action::Esc(format!("\x1b[18;{}~", modifiers_code)); - F8, mods, ~TermMode::VI; Action::Esc(format!("\x1b[19;{}~", modifiers_code)); - F9, mods, ~TermMode::VI; Action::Esc(format!("\x1b[20;{}~", modifiers_code)); - F10, mods, ~TermMode::VI; Action::Esc(format!("\x1b[21;{}~", modifiers_code)); - F11, mods, ~TermMode::VI; Action::Esc(format!("\x1b[23;{}~", modifiers_code)); - F12, mods, ~TermMode::VI; Action::Esc(format!("\x1b[24;{}~", modifiers_code)); - F13, mods, ~TermMode::VI; Action::Esc(format!("\x1b[25;{}~", modifiers_code)); - F14, mods, ~TermMode::VI; Action::Esc(format!("\x1b[26;{}~", modifiers_code)); - F15, mods, ~TermMode::VI; Action::Esc(format!("\x1b[28;{}~", modifiers_code)); - F16, mods, ~TermMode::VI; Action::Esc(format!("\x1b[29;{}~", modifiers_code)); - F17, mods, ~TermMode::VI; Action::Esc(format!("\x1b[31;{}~", modifiers_code)); - F18, mods, ~TermMode::VI; Action::Esc(format!("\x1b[32;{}~", modifiers_code)); - F19, mods, ~TermMode::VI; Action::Esc(format!("\x1b[33;{}~", modifiers_code)); - F20, mods, ~TermMode::VI; Action::Esc(format!("\x1b[34;{}~", modifiers_code)); + Delete, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[3;{}~", modifiers_code)); + Up, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[1;{}A", modifiers_code)); + Down, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[1;{}B", modifiers_code)); + Right, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[1;{}C", modifiers_code)); + Left, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[1;{}D", modifiers_code)); + F1, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[1;{}P", modifiers_code)); + F2, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[1;{}Q", modifiers_code)); + F3, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[1;{}R", modifiers_code)); + F4, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[1;{}S", modifiers_code)); + F5, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[15;{}~", modifiers_code)); + F6, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[17;{}~", modifiers_code)); + F7, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[18;{}~", modifiers_code)); + F8, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[19;{}~", modifiers_code)); + F9, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[20;{}~", modifiers_code)); + F10, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[21;{}~", modifiers_code)); + F11, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[23;{}~", modifiers_code)); + F12, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[24;{}~", modifiers_code)); + F13, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[25;{}~", modifiers_code)); + F14, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[26;{}~", modifiers_code)); + F15, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[28;{}~", modifiers_code)); + F16, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[29;{}~", modifiers_code)); + F17, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[31;{}~", modifiers_code)); + F18, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[32;{}~", modifiers_code)); + F19, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[33;{}~", modifiers_code)); + F20, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[34;{}~", modifiers_code)); )); // We're adding the following bindings with `Shift` manually above, so skipping them here. if modifiers_code != 2 { bindings.extend(bindings!( KeyBinding; - Insert, mods, ~TermMode::VI; Action::Esc(format!("\x1b[2;{}~", modifiers_code)); - PageUp, mods, ~TermMode::VI; Action::Esc(format!("\x1b[5;{}~", modifiers_code)); - PageDown, mods, ~TermMode::VI; Action::Esc(format!("\x1b[6;{}~", modifiers_code)); - End, mods, ~TermMode::VI; Action::Esc(format!("\x1b[1;{}F", modifiers_code)); - Home, mods, ~TermMode::VI; Action::Esc(format!("\x1b[1;{}H", modifiers_code)); + Insert, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[2;{}~", modifiers_code)); + PageUp, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[5;{}~", modifiers_code)); + PageDown, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[6;{}~", modifiers_code)); + End, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[1;{}F", modifiers_code)); + Home, mods, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc(format!("\x1b[1;{}H", modifiers_code)); )); } } @@ -486,12 +614,15 @@ pub fn default_key_bindings() -> Vec { fn common_keybindings() -> Vec { bindings!( KeyBinding; - V, ModifiersState::CTRL | ModifiersState::SHIFT, ~TermMode::VI; Action::Paste; + V, ModifiersState::CTRL | ModifiersState::SHIFT, ~BindingMode::VI; Action::Paste; C, ModifiersState::CTRL | ModifiersState::SHIFT; Action::Copy; - F, ModifiersState::CTRL | ModifiersState::SHIFT; Action::SearchForward; - B, ModifiersState::CTRL | ModifiersState::SHIFT; Action::SearchBackward; - C, ModifiersState::CTRL | ModifiersState::SHIFT, +TermMode::VI; Action::ClearSelection; - Insert, ModifiersState::SHIFT, ~TermMode::VI; Action::PasteSelection; + F, ModifiersState::CTRL | ModifiersState::SHIFT, ~BindingMode::SEARCH; + Action::SearchForward; + B, ModifiersState::CTRL | ModifiersState::SHIFT, ~BindingMode::SEARCH; + Action::SearchBackward; + C, ModifiersState::CTRL | ModifiersState::SHIFT, + +BindingMode::VI, ~BindingMode::SEARCH; Action::ClearSelection; + Insert, ModifiersState::SHIFT, ~BindingMode::VI; Action::PasteSelection; Key0, ModifiersState::CTRL; Action::ResetFontSize; Equals, ModifiersState::CTRL; Action::IncreaseFontSize; Plus, ModifiersState::CTRL; Action::IncreaseFontSize; @@ -520,26 +651,28 @@ pub fn platform_key_bindings() -> Vec { pub fn platform_key_bindings() -> Vec { bindings!( KeyBinding; - Key0, ModifiersState::LOGO; Action::ResetFontSize; - Equals, ModifiersState::LOGO; Action::IncreaseFontSize; - Plus, ModifiersState::LOGO; Action::IncreaseFontSize; - NumpadAdd, ModifiersState::LOGO; Action::IncreaseFontSize; - Minus, ModifiersState::LOGO; Action::DecreaseFontSize; - NumpadSubtract, ModifiersState::LOGO; Action::DecreaseFontSize; - Insert, ModifiersState::SHIFT, ~TermMode::VI; Action::Esc("\x1b[2;2~".into()); - K, ModifiersState::LOGO, ~TermMode::VI; Action::Esc("\x0c".into()); - V, ModifiersState::LOGO, ~TermMode::VI; Action::Paste; + Key0, ModifiersState::LOGO; Action::ResetFontSize; + Equals, ModifiersState::LOGO; Action::IncreaseFontSize; + Plus, ModifiersState::LOGO; Action::IncreaseFontSize; + NumpadAdd, ModifiersState::LOGO; Action::IncreaseFontSize; + Minus, ModifiersState::LOGO; Action::DecreaseFontSize; + NumpadSubtract, ModifiersState::LOGO; Action::DecreaseFontSize; + Insert, ModifiersState::SHIFT, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x1b[2;2~".into()); + K, ModifiersState::LOGO, ~BindingMode::VI, ~BindingMode::SEARCH; + Action::Esc("\x0c".into()); + K, ModifiersState::LOGO, ~BindingMode::VI, ~BindingMode::SEARCH; Action::ClearHistory; + V, ModifiersState::LOGO, ~BindingMode::VI; Action::Paste; N, ModifiersState::LOGO; Action::SpawnNewInstance; F, ModifiersState::CTRL | ModifiersState::LOGO; Action::ToggleFullscreen; - K, ModifiersState::LOGO; Action::ClearHistory; C, ModifiersState::LOGO; Action::Copy; - C, ModifiersState::LOGO, +TermMode::VI; Action::ClearSelection; + C, ModifiersState::LOGO, +BindingMode::VI, ~BindingMode::SEARCH; Action::ClearSelection; H, ModifiersState::LOGO; Action::Hide; M, ModifiersState::LOGO; Action::Minimize; Q, ModifiersState::LOGO; Action::Quit; W, ModifiersState::LOGO; Action::Quit; - F, ModifiersState::LOGO; Action::SearchForward; - B, ModifiersState::LOGO; Action::SearchBackward; + F, ModifiersState::LOGO, ~BindingMode::SEARCH; Action::SearchForward; + B, ModifiersState::LOGO, ~BindingMode::SEARCH; Action::SearchBackward; ) } @@ -572,8 +705,31 @@ impl<'a> Deserialize<'a> for Key { } struct ModeWrapper { - pub mode: TermMode, - pub not_mode: TermMode, + pub mode: BindingMode, + pub not_mode: BindingMode, +} + +bitflags! { + /// Modes available for key bindings. + pub struct BindingMode: u8 { + const APP_CURSOR = 0b0000_0001; + const APP_KEYPAD = 0b0000_0010; + const ALT_SCREEN = 0b0000_0100; + const VI = 0b0000_1000; + const SEARCH = 0b0001_0000; + } +} + +impl BindingMode { + pub fn new(mode: &TermMode, search: bool) -> BindingMode { + let mut binding_mode = BindingMode::empty(); + binding_mode.set(BindingMode::APP_CURSOR, mode.contains(TermMode::APP_CURSOR)); + binding_mode.set(BindingMode::APP_KEYPAD, mode.contains(TermMode::APP_KEYPAD)); + binding_mode.set(BindingMode::ALT_SCREEN, mode.contains(TermMode::ALT_SCREEN)); + binding_mode.set(BindingMode::VI, mode.contains(TermMode::VI)); + binding_mode.set(BindingMode::SEARCH, search); + binding_mode + } } impl<'a> Deserialize<'a> for ModeWrapper { @@ -596,18 +752,21 @@ impl<'a> Deserialize<'a> for ModeWrapper { where E: de::Error, { - let mut res = ModeWrapper { mode: TermMode::empty(), not_mode: TermMode::empty() }; + let mut res = + ModeWrapper { mode: BindingMode::empty(), not_mode: BindingMode::empty() }; for modifier in value.split('|') { match modifier.trim().to_lowercase().as_str() { - "appcursor" => res.mode |= TermMode::APP_CURSOR, - "~appcursor" => res.not_mode |= TermMode::APP_CURSOR, - "appkeypad" => res.mode |= TermMode::APP_KEYPAD, - "~appkeypad" => res.not_mode |= TermMode::APP_KEYPAD, - "alt" => res.mode |= TermMode::ALT_SCREEN, - "~alt" => res.not_mode |= TermMode::ALT_SCREEN, - "vi" => res.mode |= TermMode::VI, - "~vi" => res.not_mode |= TermMode::VI, + "appcursor" => res.mode |= BindingMode::APP_CURSOR, + "~appcursor" => res.not_mode |= BindingMode::APP_CURSOR, + "appkeypad" => res.mode |= BindingMode::APP_KEYPAD, + "~appkeypad" => res.not_mode |= BindingMode::APP_KEYPAD, + "alt" => res.mode |= BindingMode::ALT_SCREEN, + "~alt" => res.not_mode |= BindingMode::ALT_SCREEN, + "vi" => res.mode |= BindingMode::VI, + "~vi" => res.not_mode |= BindingMode::VI, + "search" => res.mode |= BindingMode::SEARCH, + "~search" => res.not_mode |= BindingMode::SEARCH, _ => return Err(E::invalid_value(Unexpected::Str(modifier), &self)), } } @@ -675,8 +834,8 @@ struct RawBinding { key: Option, mouse: Option, mods: ModifiersState, - mode: TermMode, - notmode: TermMode, + mode: BindingMode, + notmode: BindingMode, action: Action, } @@ -778,8 +937,8 @@ impl<'a> Deserialize<'a> for RawBinding { let mut key: Option = None; let mut chars: Option = None; let mut action: Option = None; - let mut mode: Option = None; - let mut not_mode: Option = None; + let mut mode: Option = None; + let mut not_mode: Option = None; let mut mouse: Option = None; let mut command: Option = None; @@ -834,6 +993,10 @@ impl<'a> Deserialize<'a> for RawBinding { Some(vi_action.into()) } else if let Ok(vi_motion) = ViMotion::deserialize(value.clone()) { Some(vi_motion.into()) + } else if let Ok(search_action) = + SearchAction::deserialize(value.clone()) + { + Some(search_action.into()) } else { match Action::deserialize(value.clone()).map_err(V::Error::custom) { Ok(action) => Some(action), @@ -883,14 +1046,15 @@ impl<'a> Deserialize<'a> for RawBinding { } } - let mode = mode.unwrap_or_else(TermMode::empty); - let not_mode = not_mode.unwrap_or_else(TermMode::empty); + let mode = mode.unwrap_or_else(BindingMode::empty); + let not_mode = not_mode.unwrap_or_else(BindingMode::empty); let mods = mods.unwrap_or_else(ModifiersState::default); let action = match (action, chars, command) { (Some(action @ Action::ViMotion(_)), None, None) | (Some(action @ Action::ViAction(_)), None, None) => { - if !mode.intersects(TermMode::VI) || not_mode.intersects(TermMode::VI) { + 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, @@ -898,6 +1062,16 @@ impl<'a> Deserialize<'a> for RawBinding { } action }, + (Some(action @ Action::SearchAction(_)), 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), None, None) => action, (None, Some(chars), None) => Action::Esc(chars), (None, None, Some(cmd)) => Action::Command(cmd), @@ -995,12 +1169,10 @@ impl<'a> de::Deserialize<'a> for ModsWrapper { #[cfg(test)] mod tests { + use super::*; + use glutin::event::ModifiersState; - use alacritty_terminal::term::TermMode; - - use crate::config::{Action, Binding}; - type MockBinding = Binding; impl Default for MockBinding { @@ -1008,8 +1180,8 @@ mod tests { Self { mods: Default::default(), action: Action::None, - mode: TermMode::empty(), - notmode: TermMode::empty(), + mode: BindingMode::empty(), + notmode: BindingMode::empty(), trigger: Default::default(), } } @@ -1048,9 +1220,9 @@ mod tests { #[test] fn binding_matches_identical_mode() { let mut b1 = MockBinding::default(); - b1.mode = TermMode::ALT_SCREEN; + b1.mode = BindingMode::ALT_SCREEN; let mut b2 = MockBinding::default(); - b2.mode = TermMode::ALT_SCREEN; + b2.mode = BindingMode::ALT_SCREEN; assert!(b1.triggers_match(&b2)); assert!(b2.triggers_match(&b1)); @@ -1060,8 +1232,8 @@ mod tests { fn binding_without_mode_matches_any_mode() { let b1 = MockBinding::default(); let mut b2 = MockBinding::default(); - b2.mode = TermMode::APP_KEYPAD; - b2.notmode = TermMode::ALT_SCREEN; + b2.mode = BindingMode::APP_KEYPAD; + b2.notmode = BindingMode::ALT_SCREEN; assert!(b1.triggers_match(&b2)); } @@ -1069,8 +1241,8 @@ mod tests { #[test] fn binding_with_mode_matches_empty_mode() { let mut b1 = MockBinding::default(); - b1.mode = TermMode::APP_KEYPAD; - b1.notmode = TermMode::ALT_SCREEN; + b1.mode = BindingMode::APP_KEYPAD; + b1.notmode = BindingMode::ALT_SCREEN; let b2 = MockBinding::default(); assert!(b1.triggers_match(&b2)); @@ -1080,9 +1252,9 @@ mod tests { #[test] fn binding_matches_modes() { let mut b1 = MockBinding::default(); - b1.mode = TermMode::ALT_SCREEN | TermMode::APP_KEYPAD; + b1.mode = BindingMode::ALT_SCREEN | BindingMode::APP_KEYPAD; let mut b2 = MockBinding::default(); - b2.mode = TermMode::APP_KEYPAD; + b2.mode = BindingMode::APP_KEYPAD; assert!(b1.triggers_match(&b2)); assert!(b2.triggers_match(&b1)); @@ -1091,9 +1263,9 @@ mod tests { #[test] fn binding_matches_partial_intersection() { let mut b1 = MockBinding::default(); - b1.mode = TermMode::ALT_SCREEN | TermMode::APP_KEYPAD; + b1.mode = BindingMode::ALT_SCREEN | BindingMode::APP_KEYPAD; let mut b2 = MockBinding::default(); - b2.mode = TermMode::APP_KEYPAD | TermMode::APP_CURSOR; + b2.mode = BindingMode::APP_KEYPAD | BindingMode::APP_CURSOR; assert!(b1.triggers_match(&b2)); assert!(b2.triggers_match(&b1)); @@ -1102,9 +1274,9 @@ mod tests { #[test] fn binding_mismatches_notmode() { let mut b1 = MockBinding::default(); - b1.mode = TermMode::ALT_SCREEN; + b1.mode = BindingMode::ALT_SCREEN; let mut b2 = MockBinding::default(); - b2.notmode = TermMode::ALT_SCREEN; + b2.notmode = BindingMode::ALT_SCREEN; assert!(!b1.triggers_match(&b2)); assert!(!b2.triggers_match(&b1)); @@ -1113,9 +1285,9 @@ mod tests { #[test] fn binding_mismatches_unrelated() { let mut b1 = MockBinding::default(); - b1.mode = TermMode::ALT_SCREEN; + b1.mode = BindingMode::ALT_SCREEN; let mut b2 = MockBinding::default(); - b2.mode = TermMode::APP_KEYPAD; + b2.mode = BindingMode::APP_KEYPAD; assert!(!b1.triggers_match(&b2)); assert!(!b2.triggers_match(&b1)); @@ -1125,8 +1297,8 @@ mod tests { fn binding_matches_notmodes() { let mut subset_notmodes = MockBinding::default(); let mut superset_notmodes = MockBinding::default(); - subset_notmodes.notmode = TermMode::VI | TermMode::APP_CURSOR; - superset_notmodes.notmode = TermMode::APP_CURSOR; + subset_notmodes.notmode = BindingMode::VI | BindingMode::APP_CURSOR; + superset_notmodes.notmode = BindingMode::APP_CURSOR; assert!(subset_notmodes.triggers_match(&superset_notmodes)); assert!(superset_notmodes.triggers_match(&subset_notmodes)); @@ -1136,9 +1308,9 @@ mod tests { fn binding_matches_mode_notmode() { let mut b1 = MockBinding::default(); let mut b2 = MockBinding::default(); - b1.mode = TermMode::VI; - b1.notmode = TermMode::APP_CURSOR; - b2.notmode = TermMode::APP_CURSOR; + b1.mode = BindingMode::VI; + b1.notmode = BindingMode::APP_CURSOR; + b2.notmode = BindingMode::APP_CURSOR; assert!(b1.triggers_match(&b2)); assert!(b2.triggers_match(&b1)); @@ -1175,26 +1347,26 @@ mod tests { #[test] fn binding_trigger_modes() { let mut binding = MockBinding::default(); - binding.mode = TermMode::ALT_SCREEN; + binding.mode = BindingMode::ALT_SCREEN; let t = binding.trigger; let mods = binding.mods; - assert!(!binding.is_triggered_by(TermMode::INSERT, mods, &t)); - assert!(binding.is_triggered_by(TermMode::ALT_SCREEN, mods, &t)); - assert!(binding.is_triggered_by(TermMode::ALT_SCREEN | TermMode::INSERT, mods, &t)); + assert!(!binding.is_triggered_by(BindingMode::VI, mods, &t)); + assert!(binding.is_triggered_by(BindingMode::ALT_SCREEN, mods, &t)); + assert!(binding.is_triggered_by(BindingMode::ALT_SCREEN | BindingMode::VI, mods, &t)); } #[test] fn binding_trigger_notmodes() { let mut binding = MockBinding::default(); - binding.notmode = TermMode::ALT_SCREEN; + binding.notmode = BindingMode::ALT_SCREEN; let t = binding.trigger; let mods = binding.mods; - assert!(binding.is_triggered_by(TermMode::INSERT, mods, &t)); - assert!(!binding.is_triggered_by(TermMode::ALT_SCREEN, mods, &t)); - assert!(!binding.is_triggered_by(TermMode::ALT_SCREEN | TermMode::INSERT, mods, &t)); + assert!(binding.is_triggered_by(BindingMode::VI, mods, &t)); + assert!(!binding.is_triggered_by(BindingMode::ALT_SCREEN, mods, &t)); + assert!(!binding.is_triggered_by(BindingMode::ALT_SCREEN | BindingMode::VI, mods, &t)); } } diff --git a/alacritty/src/config/mod.rs b/alacritty/src/config/mod.rs index 14afc7d1..19888add 100644 --- a/alacritty/src/config/mod.rs +++ b/alacritty/src/config/mod.rs @@ -20,7 +20,7 @@ mod bindings; mod mouse; use crate::cli::Options; -pub use crate::config::bindings::{Action, Binding, Key, ViAction}; +pub use crate::config::bindings::{Action, Binding, BindingMode, Key, SearchAction, ViAction}; #[cfg(test)] pub use crate::config::mouse::{ClickHandler, Mouse}; use crate::config::ui_config::UIConfig; diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs index 441cb21e..db3c84fb 100644 --- a/alacritty/src/event.rs +++ b/alacritty/src/event.rs @@ -418,6 +418,12 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext for ActionCon #[inline] fn confirm_search(&mut self) { + // Just cancel search when not in vi mode. + if !self.terminal.mode().contains(TermMode::VI) { + self.cancel_search(); + return; + } + // Force unlimited search if the previous one was interrupted. if self.scheduler.scheduled(TimerId::DelayedSearch) { self.goto_match(None); @@ -445,22 +451,24 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext for ActionCon } #[inline] - fn push_search(&mut self, c: char) { + fn search_input(&mut self, c: char) { if let Some(regex) = self.search_state.regex.as_mut() { + match c { + // Handle backspace/ctrl+h. + '\x08' | '\x7f' => { + let _ = regex.pop(); + }, + // Add ascii and unicode text. + ' '..='~' | '\u{a0}'..='\u{10ffff}' => regex.push(c), + // Ignore non-printable characters. + _ => return, + } + if !self.terminal.mode().contains(TermMode::VI) { // Clear selection so we do not obstruct any matches. self.terminal.selection = None; } - regex.push(c); - self.update_search(); - } - } - - #[inline] - fn pop_search(&mut self) { - if let Some(regex) = self.search_state.regex.as_mut() { - regex.pop(); self.update_search(); } } diff --git a/alacritty/src/input.rs b/alacritty/src/input.rs index d6296a5d..d5afbbd2 100644 --- a/alacritty/src/input.rs +++ b/alacritty/src/input.rs @@ -15,7 +15,6 @@ use log::trace; use glutin::dpi::PhysicalPosition; use glutin::event::{ ElementState, KeyboardInput, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase, - VirtualKeyCode, }; use glutin::event_loop::EventLoopWindowTarget; #[cfg(target_os = "macos")] @@ -32,7 +31,7 @@ use alacritty_terminal::term::{ClipboardType, SizeInfo, Term}; use alacritty_terminal::vi_mode::ViMotion; use crate::clipboard::Clipboard; -use crate::config::{Action, Binding, Config, Key, ViAction}; +use crate::config::{Action, Binding, BindingMode, Config, Key, SearchAction, ViAction}; use crate::daemon::start_daemon; use crate::event::{ClickState, Event, Mouse, TYPING_SEARCH_DELAY}; use crate::message_bar::{self, Message}; @@ -97,8 +96,7 @@ pub trait ActionContext { fn start_search(&mut self, direction: Direction); fn confirm_search(&mut self); fn cancel_search(&mut self); - fn push_search(&mut self, c: char); - fn pop_search(&mut self); + fn search_input(&mut self, c: char); fn pop_word_search(&mut self); fn advance_search_origin(&mut self, direction: Direction); fn search_direction(&self) -> Direction; @@ -145,17 +143,6 @@ impl Execute for Action { ctx.scroll(Scroll::Bottom); ctx.write_to_pty(s.clone().into_bytes()) }, - Action::Copy => ctx.copy_selection(ClipboardType::Clipboard), - #[cfg(not(any(target_os = "macos", windows)))] - Action::CopySelection => ctx.copy_selection(ClipboardType::Selection), - Action::Paste => { - let text = ctx.clipboard_mut().load(ClipboardType::Clipboard); - paste(ctx, &text); - }, - Action::PasteSelection => { - let text = ctx.clipboard_mut().load(ClipboardType::Selection); - paste(ctx, &text); - }, Action::Command(ref program) => { let args = program.args(); let program = program.program(); @@ -163,7 +150,6 @@ impl Execute for Action { start_daemon(program, args); }, - Action::ClearSelection => ctx.clear_selection(), Action::ToggleViMode => ctx.terminal_mut().toggle_vi_mode(), Action::ViMotion(motion) => { ctx.on_typing_start(); @@ -221,8 +207,35 @@ impl Execute for Action { ctx.terminal_mut().vi_goto_point(*regex_match.end()); } }, + Action::SearchAction(SearchAction::SearchFocusNext) => { + ctx.advance_search_origin(ctx.search_direction()); + }, + Action::SearchAction(SearchAction::SearchFocusPrevious) => { + let direction = ctx.search_direction().opposite(); + ctx.advance_search_origin(direction); + }, + Action::SearchAction(SearchAction::SearchConfirm) => ctx.confirm_search(), + Action::SearchAction(SearchAction::SearchCancel) => ctx.cancel_search(), + Action::SearchAction(SearchAction::SearchClear) => { + let direction = ctx.search_direction(); + ctx.cancel_search(); + ctx.start_search(direction); + }, + Action::SearchAction(SearchAction::SearchDeleteWord) => ctx.pop_word_search(), Action::SearchForward => ctx.start_search(Direction::Right), Action::SearchBackward => ctx.start_search(Direction::Left), + Action::Copy => ctx.copy_selection(ClipboardType::Clipboard), + #[cfg(not(any(target_os = "macos", windows)))] + Action::CopySelection => ctx.copy_selection(ClipboardType::Selection), + Action::ClearSelection => ctx.clear_selection(), + Action::Paste => { + let text = ctx.clipboard_mut().load(ClipboardType::Clipboard); + paste(ctx, &text); + }, + Action::PasteSelection => { + let text = ctx.clipboard_mut().load(ClipboardType::Selection); + paste(ctx, &text); + }, Action::ToggleFullscreen => ctx.window_mut().toggle_fullscreen(), #[cfg(target_os = "macos")] Action::ToggleSimpleFullscreen => ctx.window_mut().toggle_simple_fullscreen(), @@ -315,7 +328,11 @@ impl Execute for Action { } fn paste>(ctx: &mut A, contents: &str) { - if ctx.terminal().mode().contains(TermMode::BRACKETED_PASTE) { + if ctx.search_active() { + for c in contents.chars() { + ctx.search_input(c); + } + } else if ctx.terminal().mode().contains(TermMode::BRACKETED_PASTE) { ctx.write_to_pty(&b"\x1b[200~"[..]); ctx.write_to_pty(contents.replace("\x1b", "").into_bytes()); ctx.write_to_pty(&b"\x1b[201~"[..]); @@ -796,57 +813,14 @@ impl<'a, T: EventListener, A: ActionContext> Processor<'a, T, A> { /// Process key input. pub fn key_input(&mut self, input: KeyboardInput) { + // Reset search delay when the user is still typing. + if self.ctx.search_active() { + if let Some(timer) = self.ctx.scheduler_mut().get_mut(TimerId::DelayedSearch) { + timer.deadline = Instant::now() + TYPING_SEARCH_DELAY; + } + } + match input.state { - ElementState::Pressed if self.ctx.search_active() => { - match (input.virtual_keycode, *self.ctx.modifiers()) { - (Some(VirtualKeyCode::Back), _) => { - self.ctx.pop_search(); - *self.ctx.suppress_chars() = true; - }, - (Some(VirtualKeyCode::Return), ModifiersState::SHIFT) - if !self.ctx.terminal().mode().contains(TermMode::VI) => - { - let direction = self.ctx.search_direction().opposite(); - self.ctx.advance_search_origin(direction); - *self.ctx.suppress_chars() = true; - } - (Some(VirtualKeyCode::Return), _) - | (Some(VirtualKeyCode::J), ModifiersState::CTRL) => { - if self.ctx.terminal().mode().contains(TermMode::VI) { - self.ctx.confirm_search(); - } else { - self.ctx.advance_search_origin(self.ctx.search_direction()); - } - - *self.ctx.suppress_chars() = true; - }, - (Some(VirtualKeyCode::Escape), _) - | (Some(VirtualKeyCode::C), ModifiersState::CTRL) => { - self.ctx.cancel_search(); - *self.ctx.suppress_chars() = true; - }, - (Some(VirtualKeyCode::U), ModifiersState::CTRL) => { - let direction = self.ctx.search_direction(); - self.ctx.cancel_search(); - self.ctx.start_search(direction); - *self.ctx.suppress_chars() = true; - }, - (Some(VirtualKeyCode::H), ModifiersState::CTRL) => { - self.ctx.pop_search(); - *self.ctx.suppress_chars() = true; - }, - (Some(VirtualKeyCode::W), ModifiersState::CTRL) => { - self.ctx.pop_word_search(); - *self.ctx.suppress_chars() = true; - }, - _ => (), - } - - // Reset search delay when the user is still typing. - if let Some(timer) = self.ctx.scheduler_mut().get_mut(TimerId::DelayedSearch) { - timer.deadline = Instant::now() + TYPING_SEARCH_DELAY; - } - }, ElementState::Pressed => { *self.ctx.received_count() = 0; self.process_key_bindings(input); @@ -877,14 +851,8 @@ impl<'a, T: EventListener, A: ActionContext> Processor<'a, T, A> { let suppress_chars = *self.ctx.suppress_chars(); let search_active = self.ctx.search_active(); if suppress_chars || self.ctx.terminal().mode().contains(TermMode::VI) || search_active { - if search_active { - // Skip control characters. - let c_decimal = c as isize; - let is_printable = (c_decimal >= 0x20 && c_decimal < 0x7f) || c_decimal >= 0xa0; - - if !suppress_chars && is_printable { - self.ctx.push_search(c); - } + if search_active && !suppress_chars { + self.ctx.search_input(c); } *self.ctx.suppress_chars() = false; @@ -922,6 +890,7 @@ impl<'a, T: EventListener, A: ActionContext> Processor<'a, T, A> { /// The provided mode, mods, and key must match what is allowed by a binding /// for its action to be executed. fn process_key_bindings(&mut self, input: KeyboardInput) { + let mode = BindingMode::new(self.ctx.terminal().mode(), self.ctx.search_active()); let mods = *self.ctx.modifiers(); let mut suppress_chars = None; @@ -934,7 +903,7 @@ impl<'a, T: EventListener, A: ActionContext> Processor<'a, T, A> { _ => continue, }; - if binding.is_triggered_by(*self.ctx.terminal().mode(), mods, &key) { + if binding.is_triggered_by(mode, mods, &key) { // Binding was triggered; run the action. let binding = binding.clone(); binding.execute(&mut self.ctx); @@ -953,14 +922,9 @@ impl<'a, T: EventListener, A: ActionContext> Processor<'a, T, A> { /// The provided mode, mods, and key must match what is allowed by a binding /// for its action to be executed. fn process_mouse_bindings(&mut self, button: MouseButton) { - // Ignore bindings while search is active. - if self.ctx.search_active() { - return; - } - - let mods = *self.ctx.modifiers(); - let mode = *self.ctx.terminal().mode(); + let mode = BindingMode::new(self.ctx.terminal().mode(), self.ctx.search_active()); let mouse_mode = self.ctx.mouse_mode(); + let mods = *self.ctx.modifiers(); for i in 0..self.ctx.config().ui_config.mouse_bindings.len() { let mut binding = self.ctx.config().ui_config.mouse_bindings[i].clone(); @@ -1151,9 +1115,7 @@ mod tests { fn cancel_search(&mut self) {} - fn push_search(&mut self, _c: char) {} - - fn pop_search(&mut self) {} + fn search_input(&mut self, _c: char) {} fn pop_word_search(&mut self) {} @@ -1462,65 +1424,65 @@ mod tests { test_process_binding! { name: process_binding_nomode_shiftmod_require_shift, - binding: Binding { trigger: KEY, mods: ModifiersState::SHIFT, action: Action::from("\x1b[1;2D"), mode: TermMode::NONE, notmode: TermMode::NONE }, + binding: Binding { trigger: KEY, mods: ModifiersState::SHIFT, action: Action::from("\x1b[1;2D"), mode: BindingMode::empty(), notmode: BindingMode::empty() }, triggers: true, - mode: TermMode::NONE, + mode: BindingMode::empty(), mods: ModifiersState::SHIFT, } test_process_binding! { name: process_binding_nomode_nomod_require_shift, - binding: Binding { trigger: KEY, mods: ModifiersState::SHIFT, action: Action::from("\x1b[1;2D"), mode: TermMode::NONE, notmode: TermMode::NONE }, + binding: Binding { trigger: KEY, mods: ModifiersState::SHIFT, action: Action::from("\x1b[1;2D"), mode: BindingMode::empty(), notmode: BindingMode::empty() }, triggers: false, - mode: TermMode::NONE, + mode: BindingMode::empty(), mods: ModifiersState::empty(), } test_process_binding! { name: process_binding_nomode_controlmod, - binding: Binding { trigger: KEY, mods: ModifiersState::CTRL, action: Action::from("\x1b[1;5D"), mode: TermMode::NONE, notmode: TermMode::NONE }, + binding: Binding { trigger: KEY, mods: ModifiersState::CTRL, action: Action::from("\x1b[1;5D"), mode: BindingMode::empty(), notmode: BindingMode::empty() }, triggers: true, - mode: TermMode::NONE, + mode: BindingMode::empty(), mods: ModifiersState::CTRL, } test_process_binding! { name: process_binding_nomode_nomod_require_not_appcursor, - binding: Binding { trigger: KEY, mods: ModifiersState::empty(), action: Action::from("\x1b[D"), mode: TermMode::NONE, notmode: TermMode::APP_CURSOR }, + binding: Binding { trigger: KEY, mods: ModifiersState::empty(), action: Action::from("\x1b[D"), mode: BindingMode::empty(), notmode: BindingMode::APP_CURSOR }, triggers: true, - mode: TermMode::NONE, + mode: BindingMode::empty(), mods: ModifiersState::empty(), } test_process_binding! { name: process_binding_appcursormode_nomod_require_appcursor, - binding: Binding { trigger: KEY, mods: ModifiersState::empty(), action: Action::from("\x1bOD"), mode: TermMode::APP_CURSOR, notmode: TermMode::NONE }, + binding: Binding { trigger: KEY, mods: ModifiersState::empty(), action: Action::from("\x1bOD"), mode: BindingMode::APP_CURSOR, notmode: BindingMode::empty() }, triggers: true, - mode: TermMode::APP_CURSOR, + mode: BindingMode::APP_CURSOR, mods: ModifiersState::empty(), } test_process_binding! { name: process_binding_nomode_nomod_require_appcursor, - binding: Binding { trigger: KEY, mods: ModifiersState::empty(), action: Action::from("\x1bOD"), mode: TermMode::APP_CURSOR, notmode: TermMode::NONE }, + binding: Binding { trigger: KEY, mods: ModifiersState::empty(), action: Action::from("\x1bOD"), mode: BindingMode::APP_CURSOR, notmode: BindingMode::empty() }, triggers: false, - mode: TermMode::NONE, + mode: BindingMode::empty(), mods: ModifiersState::empty(), } test_process_binding! { name: process_binding_appcursormode_appkeypadmode_nomod_require_appcursor, - binding: Binding { trigger: KEY, mods: ModifiersState::empty(), action: Action::from("\x1bOD"), mode: TermMode::APP_CURSOR, notmode: TermMode::NONE }, + binding: Binding { trigger: KEY, mods: ModifiersState::empty(), action: Action::from("\x1bOD"), mode: BindingMode::APP_CURSOR, notmode: BindingMode::empty() }, triggers: true, - mode: TermMode::APP_CURSOR | TermMode::APP_KEYPAD, + mode: BindingMode::APP_CURSOR | BindingMode::APP_KEYPAD, mods: ModifiersState::empty(), } test_process_binding! { name: process_binding_fail_with_extra_mods, - binding: Binding { trigger: KEY, mods: ModifiersState::LOGO, action: Action::from("arst"), mode: TermMode::NONE, notmode: TermMode::NONE }, + binding: Binding { trigger: KEY, mods: ModifiersState::LOGO, action: Action::from("arst"), mode: BindingMode::empty(), notmode: BindingMode::empty() }, triggers: false, - mode: TermMode::NONE, + mode: BindingMode::empty(), mods: ModifiersState::ALT | ModifiersState::LOGO, } }