summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--alacritty.yml14
-rw-r--r--alacritty/src/config/bindings.rs16
-rw-r--r--alacritty/src/event.rs102
-rw-r--r--alacritty/src/input.rs25
-rw-r--r--alacritty_terminal/src/grid/mod.rs2
-rw-r--r--alacritty_terminal/src/grid/tests.rs4
-rw-r--r--alacritty_terminal/src/term/mod.rs13
-rw-r--r--alacritty_terminal/src/term/search.rs8
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));