diff options
-rw-r--r-- | alacritty.yml | 14 | ||||
-rw-r--r-- | alacritty/src/config/bindings.rs | 16 | ||||
-rw-r--r-- | alacritty/src/event.rs | 102 | ||||
-rw-r--r-- | alacritty/src/input.rs | 25 | ||||
-rw-r--r-- | alacritty_terminal/src/grid/mod.rs | 2 | ||||
-rw-r--r-- | alacritty_terminal/src/grid/tests.rs | 4 | ||||
-rw-r--r-- | alacritty_terminal/src/term/mod.rs | 13 | ||||
-rw-r--r-- | alacritty_terminal/src/term/search.rs | 8 |
8 files changed, 125 insertions, 59 deletions
diff --git a/alacritty.yml b/alacritty.yml index def39e25..7055cf4f 100644 --- a/alacritty.yml +++ b/alacritty.yml @@ -145,7 +145,7 @@ # Glyph offset determines the locations of the glyphs within their cells with # the default being at the bottom. Increasing `x` moves the glyph to the right, - # increasing `y` moves the glyph upwards. + # increasing `y` moves the glyph upward. #glyph_offset: # x: 0 # y: 0 @@ -480,8 +480,8 @@ # - `action`: Execute a predefined action # # - ToggleViMode -# - Search -# - SearchReverse +# - SearchForward +# - SearchBackward # - Copy # - Paste # - PasteSelection @@ -636,14 +636,16 @@ #- { 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: Search } - #- { key: Slash, mods: Shift, mode: Vi, action: SearchReverse } + #- { 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 } # (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 } @@ -671,6 +673,8 @@ #- { 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 } #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 77521318..1c07a33b 100644 --- a/alacritty/src/config/bindings.rs +++ b/alacritty/src/config/bindings.rs @@ -176,11 +176,11 @@ pub enum Action { /// Allow receiving char input. ReceiveChar, - /// Start a buffer search. - Search, + /// Start a forward buffer search. + SearchForward, - /// Start a reverse buffer search. - SearchReverse, + /// Start a backward buffer search. + SearchBackward, /// No action. None, @@ -378,8 +378,8 @@ pub fn default_key_bindings() -> Vec<KeyBinding> { D, ModifiersState::CTRL, +TermMode::VI; Action::ScrollHalfPageDown; Y, +TermMode::VI; Action::Copy; Y, +TermMode::VI; Action::ClearSelection; - Slash, +TermMode::VI; Action::Search; - Slash, ModifiersState::SHIFT, +TermMode::VI; Action::SearchReverse; + 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; @@ -487,6 +487,8 @@ fn common_keybindings() -> Vec<KeyBinding> { KeyBinding; V, ModifiersState::CTRL | ModifiersState::SHIFT, ~TermMode::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; Key0, ModifiersState::CTRL; Action::ResetFontSize; @@ -532,6 +534,8 @@ pub fn platform_key_bindings() -> Vec<KeyBinding> { 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; ) } diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs index eed5624f..f06b818e 100644 --- a/alacritty/src/event.rs +++ b/alacritty/src/event.rs @@ -31,7 +31,7 @@ use font::{self, Size}; use alacritty_terminal::config::LOG_TARGET_CONFIG; use alacritty_terminal::event::{Event as TerminalEvent, EventListener, Notify, OnResize}; use alacritty_terminal::grid::{Dimensions, Scroll}; -use alacritty_terminal::index::{Column, Direction, Line, Point, Side}; +use alacritty_terminal::index::{Boundary, Column, Direction, Line, Point, Side}; use alacritty_terminal::selection::{Selection, SelectionType}; use alacritty_terminal::sync::FairMutex; use alacritty_terminal::term::cell::Cell; @@ -91,8 +91,8 @@ pub struct SearchState { /// Change in display offset since the beginning of the search. display_offset_delta: isize, - /// Vi cursor position before search. - vi_cursor_point: Point, + /// Search origin in viewport coordinates relative to original display offset. + origin: Point, } impl SearchState { @@ -106,7 +106,7 @@ impl Default for SearchState { Self { direction: Direction::Right, display_offset_delta: 0, - vi_cursor_point: Point::default(), + origin: Point::default(), regex: None, } } @@ -142,8 +142,16 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon } fn scroll(&mut self, scroll: Scroll) { + let old_offset = self.terminal.grid().display_offset() as isize; + self.terminal.scroll_display(scroll); + // Keep track of manual display offset changes during search. + if self.search_active() { + let display_offset = self.terminal.grid().display_offset(); + self.search_state.display_offset_delta += old_offset - display_offset as isize; + } + // Update selection. if self.terminal.mode().contains(TermMode::VI) && self.terminal.selection.as_ref().map(|s| s.is_empty()) != Some(true) @@ -339,10 +347,14 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon self.search_state.regex = Some(String::new()); self.search_state.direction = direction; - // Store original vi cursor position as search origin and for resetting. - self.search_state.vi_cursor_point = if self.terminal.mode().contains(TermMode::VI) { + // Store original search position as origin and reset location. + self.search_state.display_offset_delta = 0; + self.search_state.origin = if self.terminal.mode().contains(TermMode::VI) { self.terminal.vi_mode_cursor.point } else { + // Clear search, since it is used as the active match. + self.terminal.selection = None; + match direction { Direction::Right => Point::new(Line(0), Column(0)), Direction::Left => Point::new(num_lines - 2, num_cols - 1), @@ -355,9 +367,6 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon #[inline] fn confirm_search(&mut self) { - // Enter vi mode once search is confirmed. - self.terminal.set_vi_mode(); - // Force unlimited search if the previous one was interrupted. if self.scheduler.scheduled(TimerId::DelayedSearch) { self.goto_match(None); @@ -368,9 +377,6 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon self.terminal.vi_mode_cursor.point.line += 1; } - // Clear reset state. - self.search_state.display_offset_delta = 0; - self.display_update_pending.dirty = true; self.search_state.regex = None; self.terminal.dirty = true; @@ -419,6 +425,29 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon } #[inline] + fn advance_search_origin(&mut self, direction: Direction) { + let origin = self.absolute_origin(); + self.terminal.scroll_to_point(origin); + + // Move the search origin right in front of the next match in the specified direction. + if let Some(regex_match) = self.terminal.search_next(origin, direction, Side::Left, None) { + let origin = match direction { + Direction::Right => *regex_match.end(), + Direction::Left => { + regex_match.start().sub_absolute(self.terminal, Boundary::Wrap, 1) + }, + }; + self.terminal.scroll_to_point(origin); + + let origin_relative = self.terminal.grid().clamp_buffer_to_visible(origin); + self.search_state.origin = origin_relative; + self.search_state.display_offset_delta = 0; + + self.update_search(); + } + } + + #[inline] fn search_direction(&self) -> Direction { self.search_state.direction } @@ -487,10 +516,10 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> { self.search_state.display_offset_delta = 0; // Reset vi mode cursor. - let mut vi_cursor_point = self.search_state.vi_cursor_point; - vi_cursor_point.line = min(vi_cursor_point.line, self.terminal.screen_lines() - 1); - vi_cursor_point.col = min(vi_cursor_point.col, self.terminal.cols() - 1); - self.terminal.vi_mode_cursor.point = vi_cursor_point; + let mut origin = self.search_state.origin; + origin.line = min(origin.line, self.terminal.screen_lines() - 1); + origin.col = min(origin.col, self.terminal.cols() - 1); + self.terminal.vi_mode_cursor.point = origin; // Unschedule pending timers. self.scheduler.unschedule(TimerId::DelayedSearch); @@ -506,19 +535,23 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> { // Limit search only when enough lines are available to run into the limit. limit = limit.filter(|&limit| limit <= self.terminal.total_lines()); - // Use original position as search origin. - let mut vi_cursor_point = self.search_state.vi_cursor_point; - vi_cursor_point.line = min(vi_cursor_point.line, self.terminal.screen_lines() - 1); - let mut origin = self.terminal.visible_to_buffer(vi_cursor_point); - origin.line = (origin.line as isize + self.search_state.display_offset_delta) as usize; - // Jump to the next match. let direction = self.search_state.direction; - match self.terminal.search_next(origin, direction, Side::Left, limit) { + match self.terminal.search_next(self.absolute_origin(), direction, Side::Left, limit) { Some(regex_match) => { let old_offset = self.terminal.grid().display_offset() as isize; - self.terminal.vi_goto_point(*regex_match.start()); + if self.terminal.mode().contains(TermMode::VI) { + // Move vi cursor to the start of the match. + self.terminal.vi_goto_point(*regex_match.start()); + } else { + // Select the match when vi mode is not active. + self.terminal.scroll_to_point(*regex_match.start()); + let start = self.terminal.grid().clamp_buffer_to_visible(*regex_match.start()); + let end = self.terminal.grid().clamp_buffer_to_visible(*regex_match.end()); + self.start_selection(SelectionType::Simple, start, Side::Left); + self.update_selection(end, Side::Right); + } // Store number of lines the viewport had to be moved. let display_offset = self.terminal.grid().display_offset(); @@ -544,6 +577,19 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> { self.search_state.regex = Some(regex); } + + /// Get the absolute position of the search origin. + /// + /// This takes the relative motion of the viewport since the start of the search into account. + /// So while the absolute point of the origin might have changed since new content was printed, + /// this will still return the correct absolute position. + fn absolute_origin(&self) -> Point<usize> { + let mut relative_origin = self.search_state.origin; + relative_origin.line = min(relative_origin.line, self.terminal.screen_lines() - 1); + let mut origin = self.terminal.visible_to_buffer(relative_origin); + origin.line = (origin.line as isize + self.search_state.display_offset_delta) as usize; + origin + } } #[derive(Debug, Eq, PartialEq)] @@ -1047,9 +1093,11 @@ impl<N: Notify + OnResize> Processor<N> { // Compute cursor positions before resize. let num_lines = terminal.screen_lines(); let cursor_at_bottom = terminal.grid().cursor.point.line + 1 == num_lines; - let origin_at_bottom = (!terminal.mode().contains(TermMode::VI) - && self.search_state.direction == Direction::Left) - || terminal.vi_mode_cursor.point.line == num_lines - 1; + let origin_at_bottom = if terminal.mode().contains(TermMode::VI) { + terminal.vi_mode_cursor.point.line == num_lines - 1 + } else { + self.search_state.direction == Direction::Left + }; self.display.handle_update( terminal, diff --git a/alacritty/src/input.rs b/alacritty/src/input.rs index 58ca42cb..80f3e5d9 100644 --- a/alacritty/src/input.rs +++ b/alacritty/src/input.rs @@ -100,6 +100,7 @@ pub trait ActionContext<T: EventListener> { fn push_search(&mut self, c: char); fn pop_search(&mut self); fn pop_word_search(&mut self); + fn advance_search_origin(&mut self, direction: Direction); fn search_direction(&self) -> Direction; fn search_active(&self) -> bool; } @@ -224,8 +225,8 @@ impl<T: EventListener> Execute<T> for Action { ctx.terminal_mut().vi_goto_point(*regex_match.end()); } }, - Action::Search => ctx.start_search(Direction::Right), - Action::SearchReverse => ctx.start_search(Direction::Left), + Action::SearchForward => ctx.start_search(Direction::Right), + Action::SearchBackward => ctx.start_search(Direction::Left), Action::ToggleFullscreen => ctx.window_mut().toggle_fullscreen(), #[cfg(target_os = "macos")] Action::ToggleSimpleFullscreen => ctx.window_mut().toggle_simple_fullscreen(), @@ -359,12 +360,13 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> { #[inline] pub fn mouse_moved(&mut self, position: PhysicalPosition<f64>) { + let search_active = self.ctx.search_active(); let size_info = self.ctx.size_info(); let (x, y) = position.into(); let lmb_pressed = self.ctx.mouse().left_button_state == ElementState::Pressed; - if !self.ctx.selection_is_empty() && lmb_pressed { + if !self.ctx.selection_is_empty() && lmb_pressed && !search_active { self.update_selection_scrolling(y); } @@ -405,6 +407,7 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> { let last_term_line = self.ctx.terminal().grid().screen_lines() - 1; if (lmb_pressed || self.ctx.mouse().right_button_state == ElementState::Pressed) && (self.ctx.modifiers().shift() || !self.ctx.mouse_mode()) + && !search_active { // Treat motion over message bar like motion over the last line. let line = min(point.line, last_term_line); @@ -811,9 +814,21 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> { 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) => { - self.ctx.confirm_search(); + 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), _) => { @@ -1143,6 +1158,8 @@ mod tests { fn pop_word_search(&mut self) {} + fn advance_search_origin(&mut self, _direction: Direction) {} + fn search_direction(&self) -> Direction { Direction::Right } diff --git a/alacritty_terminal/src/grid/mod.rs b/alacritty_terminal/src/grid/mod.rs index c1f980e8..5ab25c78 100644 --- a/alacritty_terminal/src/grid/mod.rs +++ b/alacritty_terminal/src/grid/mod.rs @@ -252,7 +252,7 @@ impl<T: GridCell + Default + PartialEq + Copy> Grid<T> { } } - /// Move lines at the bottom towards the top. + /// Move lines at the bottom toward the top. /// /// This is the performance-sensitive part of scrolling. pub fn scroll_up(&mut self, region: &Range<Line>, positions: Line, template: T) { diff --git a/alacritty_terminal/src/grid/tests.rs b/alacritty_terminal/src/grid/tests.rs index 1ed279a0..f86c77b5 100644 --- a/alacritty_terminal/src/grid/tests.rs +++ b/alacritty_terminal/src/grid/tests.rs @@ -56,7 +56,7 @@ fn visible_to_buffer() { assert_eq!(point, Point::new(4, Column(3))); } -// Scroll up moves lines upwards. +// Scroll up moves lines upward. #[test] fn scroll_up() { let mut grid = Grid::new(Line(10), Column(1), 0, 0); @@ -88,7 +88,7 @@ fn scroll_up() { assert_eq!(grid[Line(9)].occ, 0); } -// Scroll down moves lines downwards. +// Scroll down moves lines downward. #[test] fn scroll_down() { let mut grid = Grid::new(Line(10), Column(1), 0, 0); diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 5a59b55b..91748359 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -725,7 +725,7 @@ pub struct Term<T> { /// term is set. title_stack: Vec<Option<String>>, - /// Current forwards and backwards buffer search regexes. + /// Current forward and backward buffer search regexes. regex_search: Option<RegexSearch>, } @@ -1115,13 +1115,6 @@ impl<T> Term<T> { self.dirty = true; } - /// Start vi mode without moving the cursor. - #[inline] - pub fn set_vi_mode(&mut self) { - self.mode.insert(TermMode::VI); - self.dirty = true; - } - /// Move vi mode cursor. #[inline] pub fn vi_motion(&mut self, motion: ViMotion) @@ -1466,8 +1459,8 @@ impl<T: EventListener> Handler for Term<T> { ptr::copy(src, dst, num_cells); } - // Cells were just moved out towards the end of the line; fill in - // between source and dest with blanks. + // Cells were just moved out toward the end of the line; + // fill in between source and dest with blanks. for c in &mut line[source..destination] { c.reset(&cursor.template); } diff --git a/alacritty_terminal/src/term/search.rs b/alacritty_terminal/src/term/search.rs index b1766b05..a6664054 100644 --- a/alacritty_terminal/src/term/search.rs +++ b/alacritty_terminal/src/term/search.rs @@ -28,7 +28,7 @@ pub struct RegexSearch { } impl RegexSearch { - /// Build the forwards and backwards search DFAs. + /// Build the forward and backward search DFAs. pub fn new(search: &str) -> Result<RegexSearch, RegexError> { // Check case info for smart case let has_uppercase = search.chars().any(|c| c.is_uppercase()); @@ -318,7 +318,7 @@ impl<T> Term<T> { let start_char = self.grid[point.line][point.col].c; // Find the matching bracket we're looking for - let (forwards, end_char) = BRACKET_PAIRS.iter().find_map(|(open, close)| { + let (forward, end_char) = BRACKET_PAIRS.iter().find_map(|(open, close)| { if open == &start_char { Some((true, *close)) } else if close == &start_char { @@ -336,7 +336,7 @@ impl<T> Term<T> { loop { // Check the next cell - let cell = if forwards { iter.next() } else { iter.prev() }; + let cell = if forward { iter.next() } else { iter.prev() }; // Break if there are no more cells let c = match cell { @@ -633,7 +633,7 @@ mod tests { fn reverse_search_dead_recovery() { let mut term = mock_term("zooo lense"); - // Make sure the reverse DFA operates the same as a forwards DFA. + // Make sure the reverse DFA operates the same as a forward DFA. term.regex_search = Some(RegexSearch::new("zoo").unwrap()); let start = Point::new(0, Column(9)); let end = Point::new(0, Column(0)); |