diff options
Diffstat (limited to 'alacritty/src/input.rs')
-rw-r--r-- | alacritty/src/input.rs | 166 |
1 files changed, 64 insertions, 102 deletions
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<T: EventListener> { 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<T: EventListener> Execute<T> 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<T: EventListener> Execute<T> 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<T: EventListener> Execute<T> 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<T: EventListener> Execute<T> for Action { } fn paste<T: EventListener, A: ActionContext<T>>(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<T>> Processor<'a, T, A> { /// Process key input. pub fn key_input(&mut self, input: KeyboardInput) { - 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 self.ctx.search_active() { + if let Some(timer) = self.ctx.scheduler_mut().get_mut(TimerId::DelayedSearch) { + timer.deadline = Instant::now() + TYPING_SEARCH_DELAY; + } + } - // 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; - } - }, + match input.state { ElementState::Pressed => { *self.ctx.received_count() = 0; self.process_key_bindings(input); @@ -877,14 +851,8 @@ impl<'a, T: EventListener, A: ActionContext<T>> 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<T>> 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<T>> 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<T>> 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, } } |