mirror of
https://github.com/alacritty/alacritty.git
synced 2024-11-18 13:55:23 -05:00
Add support for searching without vi mode
This implements search without vi mode by using the selection to track the active search match and advancing it on user input. The keys to go to the next or previous match are not configurable and are bound to enter and shift enter based on Firefox's behavior. Fixes #3937.
This commit is contained in:
parent
d868848ef1
commit
142f84efb9
8 changed files with 125 additions and 59 deletions
|
@ -145,7 +145,7 @@
|
||||||
|
|
||||||
# Glyph offset determines the locations of the glyphs within their cells with
|
# 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,
|
# 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:
|
#glyph_offset:
|
||||||
# x: 0
|
# x: 0
|
||||||
# y: 0
|
# y: 0
|
||||||
|
@ -480,8 +480,8 @@
|
||||||
# - `action`: Execute a predefined action
|
# - `action`: Execute a predefined action
|
||||||
#
|
#
|
||||||
# - ToggleViMode
|
# - ToggleViMode
|
||||||
# - Search
|
# - SearchForward
|
||||||
# - SearchReverse
|
# - SearchBackward
|
||||||
# - Copy
|
# - Copy
|
||||||
# - Paste
|
# - Paste
|
||||||
# - PasteSelection
|
# - PasteSelection
|
||||||
|
@ -636,14 +636,16 @@
|
||||||
#- { key: W, mods: Shift, mode: Vi, action: WordRight }
|
#- { key: W, mods: Shift, mode: Vi, action: WordRight }
|
||||||
#- { key: E, mods: Shift, mode: Vi, action: WordRightEnd }
|
#- { key: E, mods: Shift, mode: Vi, action: WordRightEnd }
|
||||||
#- { key: Key5, mods: Shift, mode: Vi, action: Bracket }
|
#- { key: Key5, mods: Shift, mode: Vi, action: Bracket }
|
||||||
#- { key: Slash, mode: Vi, action: Search }
|
#- { key: Slash, mode: Vi, action: SearchForward }
|
||||||
#- { key: Slash, mods: Shift, mode: Vi, action: SearchReverse }
|
#- { key: Slash, mods: Shift, mode: Vi, action: SearchBackward }
|
||||||
#- { key: N, mode: Vi, action: SearchNext }
|
#- { key: N, mode: Vi, action: SearchNext }
|
||||||
#- { key: N, mods: Shift, mode: Vi, action: SearchPrevious }
|
#- { key: N, mods: Shift, mode: Vi, action: SearchPrevious }
|
||||||
|
|
||||||
# (Windows, Linux, and BSD only)
|
# (Windows, Linux, and BSD only)
|
||||||
#- { key: V, mods: Control|Shift, action: Paste }
|
#- { key: V, mods: Control|Shift, action: Paste }
|
||||||
#- { key: C, mods: Control|Shift, action: Copy }
|
#- { 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: C, mods: Control|Shift, mode: Vi, action: ClearSelection }
|
||||||
#- { key: Insert, mods: Shift, action: PasteSelection }
|
#- { key: Insert, mods: Shift, action: PasteSelection }
|
||||||
#- { key: Key0, mods: Control, action: ResetFontSize }
|
#- { key: Key0, mods: Control, action: ResetFontSize }
|
||||||
|
@ -671,6 +673,8 @@
|
||||||
#- { key: W, mods: Command, action: Quit }
|
#- { key: W, mods: Command, action: Quit }
|
||||||
#- { key: N, mods: Command, action: SpawnNewInstance }
|
#- { key: N, mods: Command, action: SpawnNewInstance }
|
||||||
#- { key: F, mods: Command|Control, action: ToggleFullscreen }
|
#- { key: F, mods: Command|Control, action: ToggleFullscreen }
|
||||||
|
#- { key: F, mods: Command, action: SearchForward }
|
||||||
|
#- { key: B, mods: Command, action: SearchBackward }
|
||||||
|
|
||||||
#debug:
|
#debug:
|
||||||
# Display the time it takes to redraw each frame.
|
# Display the time it takes to redraw each frame.
|
||||||
|
|
|
@ -176,11 +176,11 @@ pub enum Action {
|
||||||
/// Allow receiving char input.
|
/// Allow receiving char input.
|
||||||
ReceiveChar,
|
ReceiveChar,
|
||||||
|
|
||||||
/// Start a buffer search.
|
/// Start a forward buffer search.
|
||||||
Search,
|
SearchForward,
|
||||||
|
|
||||||
/// Start a reverse buffer search.
|
/// Start a backward buffer search.
|
||||||
SearchReverse,
|
SearchBackward,
|
||||||
|
|
||||||
/// No action.
|
/// No action.
|
||||||
None,
|
None,
|
||||||
|
@ -378,8 +378,8 @@ pub fn default_key_bindings() -> Vec<KeyBinding> {
|
||||||
D, ModifiersState::CTRL, +TermMode::VI; Action::ScrollHalfPageDown;
|
D, ModifiersState::CTRL, +TermMode::VI; Action::ScrollHalfPageDown;
|
||||||
Y, +TermMode::VI; Action::Copy;
|
Y, +TermMode::VI; Action::Copy;
|
||||||
Y, +TermMode::VI; Action::ClearSelection;
|
Y, +TermMode::VI; Action::ClearSelection;
|
||||||
Slash, +TermMode::VI; Action::Search;
|
Slash, +TermMode::VI; Action::SearchForward;
|
||||||
Slash, ModifiersState::SHIFT, +TermMode::VI; Action::SearchReverse;
|
Slash, ModifiersState::SHIFT, +TermMode::VI; Action::SearchBackward;
|
||||||
V, +TermMode::VI; ViAction::ToggleNormalSelection;
|
V, +TermMode::VI; ViAction::ToggleNormalSelection;
|
||||||
V, ModifiersState::SHIFT, +TermMode::VI; ViAction::ToggleLineSelection;
|
V, ModifiersState::SHIFT, +TermMode::VI; ViAction::ToggleLineSelection;
|
||||||
V, ModifiersState::CTRL, +TermMode::VI; ViAction::ToggleBlockSelection;
|
V, ModifiersState::CTRL, +TermMode::VI; ViAction::ToggleBlockSelection;
|
||||||
|
@ -487,6 +487,8 @@ fn common_keybindings() -> Vec<KeyBinding> {
|
||||||
KeyBinding;
|
KeyBinding;
|
||||||
V, ModifiersState::CTRL | ModifiersState::SHIFT, ~TermMode::VI; Action::Paste;
|
V, ModifiersState::CTRL | ModifiersState::SHIFT, ~TermMode::VI; Action::Paste;
|
||||||
C, ModifiersState::CTRL | ModifiersState::SHIFT; Action::Copy;
|
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;
|
C, ModifiersState::CTRL | ModifiersState::SHIFT, +TermMode::VI; Action::ClearSelection;
|
||||||
Insert, ModifiersState::SHIFT, ~TermMode::VI; Action::PasteSelection;
|
Insert, ModifiersState::SHIFT, ~TermMode::VI; Action::PasteSelection;
|
||||||
Key0, ModifiersState::CTRL; Action::ResetFontSize;
|
Key0, ModifiersState::CTRL; Action::ResetFontSize;
|
||||||
|
@ -532,6 +534,8 @@ pub fn platform_key_bindings() -> Vec<KeyBinding> {
|
||||||
M, ModifiersState::LOGO; Action::Minimize;
|
M, ModifiersState::LOGO; Action::Minimize;
|
||||||
Q, ModifiersState::LOGO; Action::Quit;
|
Q, ModifiersState::LOGO; Action::Quit;
|
||||||
W, ModifiersState::LOGO; Action::Quit;
|
W, ModifiersState::LOGO; Action::Quit;
|
||||||
|
F, ModifiersState::LOGO; Action::SearchForward;
|
||||||
|
B, ModifiersState::LOGO; Action::SearchBackward;
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ use font::{self, Size};
|
||||||
use alacritty_terminal::config::LOG_TARGET_CONFIG;
|
use alacritty_terminal::config::LOG_TARGET_CONFIG;
|
||||||
use alacritty_terminal::event::{Event as TerminalEvent, EventListener, Notify, OnResize};
|
use alacritty_terminal::event::{Event as TerminalEvent, EventListener, Notify, OnResize};
|
||||||
use alacritty_terminal::grid::{Dimensions, Scroll};
|
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::selection::{Selection, SelectionType};
|
||||||
use alacritty_terminal::sync::FairMutex;
|
use alacritty_terminal::sync::FairMutex;
|
||||||
use alacritty_terminal::term::cell::Cell;
|
use alacritty_terminal::term::cell::Cell;
|
||||||
|
@ -91,8 +91,8 @@ pub struct SearchState {
|
||||||
/// Change in display offset since the beginning of the search.
|
/// Change in display offset since the beginning of the search.
|
||||||
display_offset_delta: isize,
|
display_offset_delta: isize,
|
||||||
|
|
||||||
/// Vi cursor position before search.
|
/// Search origin in viewport coordinates relative to original display offset.
|
||||||
vi_cursor_point: Point,
|
origin: Point,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SearchState {
|
impl SearchState {
|
||||||
|
@ -106,7 +106,7 @@ impl Default for SearchState {
|
||||||
Self {
|
Self {
|
||||||
direction: Direction::Right,
|
direction: Direction::Right,
|
||||||
display_offset_delta: 0,
|
display_offset_delta: 0,
|
||||||
vi_cursor_point: Point::default(),
|
origin: Point::default(),
|
||||||
regex: None,
|
regex: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,8 +142,16 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll(&mut self, scroll: Scroll) {
|
fn scroll(&mut self, scroll: Scroll) {
|
||||||
|
let old_offset = self.terminal.grid().display_offset() as isize;
|
||||||
|
|
||||||
self.terminal.scroll_display(scroll);
|
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.
|
// Update selection.
|
||||||
if self.terminal.mode().contains(TermMode::VI)
|
if self.terminal.mode().contains(TermMode::VI)
|
||||||
&& self.terminal.selection.as_ref().map(|s| s.is_empty()) != Some(true)
|
&& 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.regex = Some(String::new());
|
||||||
self.search_state.direction = direction;
|
self.search_state.direction = direction;
|
||||||
|
|
||||||
// Store original vi cursor position as search origin and for resetting.
|
// Store original search position as origin and reset location.
|
||||||
self.search_state.vi_cursor_point = if self.terminal.mode().contains(TermMode::VI) {
|
self.search_state.display_offset_delta = 0;
|
||||||
|
self.search_state.origin = if self.terminal.mode().contains(TermMode::VI) {
|
||||||
self.terminal.vi_mode_cursor.point
|
self.terminal.vi_mode_cursor.point
|
||||||
} else {
|
} else {
|
||||||
|
// Clear search, since it is used as the active match.
|
||||||
|
self.terminal.selection = None;
|
||||||
|
|
||||||
match direction {
|
match direction {
|
||||||
Direction::Right => Point::new(Line(0), Column(0)),
|
Direction::Right => Point::new(Line(0), Column(0)),
|
||||||
Direction::Left => Point::new(num_lines - 2, num_cols - 1),
|
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]
|
#[inline]
|
||||||
fn confirm_search(&mut self) {
|
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.
|
// Force unlimited search if the previous one was interrupted.
|
||||||
if self.scheduler.scheduled(TimerId::DelayedSearch) {
|
if self.scheduler.scheduled(TimerId::DelayedSearch) {
|
||||||
self.goto_match(None);
|
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;
|
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.display_update_pending.dirty = true;
|
||||||
self.search_state.regex = None;
|
self.search_state.regex = None;
|
||||||
self.terminal.dirty = true;
|
self.terminal.dirty = true;
|
||||||
|
@ -418,6 +424,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]
|
#[inline]
|
||||||
fn search_direction(&self) -> Direction {
|
fn search_direction(&self) -> Direction {
|
||||||
self.search_state.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;
|
self.search_state.display_offset_delta = 0;
|
||||||
|
|
||||||
// Reset vi mode cursor.
|
// Reset vi mode cursor.
|
||||||
let mut vi_cursor_point = self.search_state.vi_cursor_point;
|
let mut origin = self.search_state.origin;
|
||||||
vi_cursor_point.line = min(vi_cursor_point.line, self.terminal.screen_lines() - 1);
|
origin.line = min(origin.line, self.terminal.screen_lines() - 1);
|
||||||
vi_cursor_point.col = min(vi_cursor_point.col, self.terminal.cols() - 1);
|
origin.col = min(origin.col, self.terminal.cols() - 1);
|
||||||
self.terminal.vi_mode_cursor.point = vi_cursor_point;
|
self.terminal.vi_mode_cursor.point = origin;
|
||||||
|
|
||||||
// Unschedule pending timers.
|
// Unschedule pending timers.
|
||||||
self.scheduler.unschedule(TimerId::DelayedSearch);
|
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 search only when enough lines are available to run into the limit.
|
||||||
limit = limit.filter(|&limit| limit <= self.terminal.total_lines());
|
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.
|
// Jump to the next match.
|
||||||
let direction = self.search_state.direction;
|
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) => {
|
Some(regex_match) => {
|
||||||
let old_offset = self.terminal.grid().display_offset() as isize;
|
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.
|
// Store number of lines the viewport had to be moved.
|
||||||
let display_offset = self.terminal.grid().display_offset();
|
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);
|
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)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
@ -1047,9 +1093,11 @@ impl<N: Notify + OnResize> Processor<N> {
|
||||||
// Compute cursor positions before resize.
|
// Compute cursor positions before resize.
|
||||||
let num_lines = terminal.screen_lines();
|
let num_lines = terminal.screen_lines();
|
||||||
let cursor_at_bottom = terminal.grid().cursor.point.line + 1 == num_lines;
|
let cursor_at_bottom = terminal.grid().cursor.point.line + 1 == num_lines;
|
||||||
let origin_at_bottom = (!terminal.mode().contains(TermMode::VI)
|
let origin_at_bottom = if terminal.mode().contains(TermMode::VI) {
|
||||||
&& self.search_state.direction == Direction::Left)
|
terminal.vi_mode_cursor.point.line == num_lines - 1
|
||||||
|| terminal.vi_mode_cursor.point.line == num_lines - 1;
|
} else {
|
||||||
|
self.search_state.direction == Direction::Left
|
||||||
|
};
|
||||||
|
|
||||||
self.display.handle_update(
|
self.display.handle_update(
|
||||||
terminal,
|
terminal,
|
||||||
|
|
|
@ -100,6 +100,7 @@ pub trait ActionContext<T: EventListener> {
|
||||||
fn push_search(&mut self, c: char);
|
fn push_search(&mut self, c: char);
|
||||||
fn pop_search(&mut self);
|
fn pop_search(&mut self);
|
||||||
fn pop_word_search(&mut self);
|
fn pop_word_search(&mut self);
|
||||||
|
fn advance_search_origin(&mut self, direction: Direction);
|
||||||
fn search_direction(&self) -> Direction;
|
fn search_direction(&self) -> Direction;
|
||||||
fn search_active(&self) -> bool;
|
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());
|
ctx.terminal_mut().vi_goto_point(*regex_match.end());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Action::Search => ctx.start_search(Direction::Right),
|
Action::SearchForward => ctx.start_search(Direction::Right),
|
||||||
Action::SearchReverse => ctx.start_search(Direction::Left),
|
Action::SearchBackward => ctx.start_search(Direction::Left),
|
||||||
Action::ToggleFullscreen => ctx.window_mut().toggle_fullscreen(),
|
Action::ToggleFullscreen => ctx.window_mut().toggle_fullscreen(),
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
Action::ToggleSimpleFullscreen => ctx.window_mut().toggle_simple_fullscreen(),
|
Action::ToggleSimpleFullscreen => ctx.window_mut().toggle_simple_fullscreen(),
|
||||||
|
@ -359,12 +360,13 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn mouse_moved(&mut self, position: PhysicalPosition<f64>) {
|
pub fn mouse_moved(&mut self, position: PhysicalPosition<f64>) {
|
||||||
|
let search_active = self.ctx.search_active();
|
||||||
let size_info = self.ctx.size_info();
|
let size_info = self.ctx.size_info();
|
||||||
|
|
||||||
let (x, y) = position.into();
|
let (x, y) = position.into();
|
||||||
|
|
||||||
let lmb_pressed = self.ctx.mouse().left_button_state == ElementState::Pressed;
|
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);
|
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;
|
let last_term_line = self.ctx.terminal().grid().screen_lines() - 1;
|
||||||
if (lmb_pressed || self.ctx.mouse().right_button_state == ElementState::Pressed)
|
if (lmb_pressed || self.ctx.mouse().right_button_state == ElementState::Pressed)
|
||||||
&& (self.ctx.modifiers().shift() || !self.ctx.mouse_mode())
|
&& (self.ctx.modifiers().shift() || !self.ctx.mouse_mode())
|
||||||
|
&& !search_active
|
||||||
{
|
{
|
||||||
// Treat motion over message bar like motion over the last line.
|
// Treat motion over message bar like motion over the last line.
|
||||||
let line = min(point.line, last_term_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.pop_search();
|
||||||
*self.ctx.suppress_chars() = true;
|
*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::Return), _)
|
||||||
| (Some(VirtualKeyCode::J), ModifiersState::CTRL) => {
|
| (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;
|
*self.ctx.suppress_chars() = true;
|
||||||
},
|
},
|
||||||
(Some(VirtualKeyCode::Escape), _) => {
|
(Some(VirtualKeyCode::Escape), _) => {
|
||||||
|
@ -1143,6 +1158,8 @@ mod tests {
|
||||||
|
|
||||||
fn pop_word_search(&mut self) {}
|
fn pop_word_search(&mut self) {}
|
||||||
|
|
||||||
|
fn advance_search_origin(&mut self, _direction: Direction) {}
|
||||||
|
|
||||||
fn search_direction(&self) -> Direction {
|
fn search_direction(&self) -> Direction {
|
||||||
Direction::Right
|
Direction::Right
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
/// This is the performance-sensitive part of scrolling.
|
||||||
pub fn scroll_up(&mut self, region: &Range<Line>, positions: Line, template: T) {
|
pub fn scroll_up(&mut self, region: &Range<Line>, positions: Line, template: T) {
|
||||||
|
|
|
@ -56,7 +56,7 @@ fn visible_to_buffer() {
|
||||||
assert_eq!(point, Point::new(4, Column(3)));
|
assert_eq!(point, Point::new(4, Column(3)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scroll up moves lines upwards.
|
// Scroll up moves lines upward.
|
||||||
#[test]
|
#[test]
|
||||||
fn scroll_up() {
|
fn scroll_up() {
|
||||||
let mut grid = Grid::new(Line(10), Column(1), 0, 0);
|
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);
|
assert_eq!(grid[Line(9)].occ, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scroll down moves lines downwards.
|
// Scroll down moves lines downward.
|
||||||
#[test]
|
#[test]
|
||||||
fn scroll_down() {
|
fn scroll_down() {
|
||||||
let mut grid = Grid::new(Line(10), Column(1), 0, 0);
|
let mut grid = Grid::new(Line(10), Column(1), 0, 0);
|
||||||
|
|
|
@ -725,7 +725,7 @@ pub struct Term<T> {
|
||||||
/// term is set.
|
/// term is set.
|
||||||
title_stack: Vec<Option<String>>,
|
title_stack: Vec<Option<String>>,
|
||||||
|
|
||||||
/// Current forwards and backwards buffer search regexes.
|
/// Current forward and backward buffer search regexes.
|
||||||
regex_search: Option<RegexSearch>,
|
regex_search: Option<RegexSearch>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1115,13 +1115,6 @@ impl<T> Term<T> {
|
||||||
self.dirty = true;
|
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.
|
/// Move vi mode cursor.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn vi_motion(&mut self, motion: ViMotion)
|
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);
|
ptr::copy(src, dst, num_cells);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cells were just moved out towards the end of the line; fill in
|
// Cells were just moved out toward the end of the line;
|
||||||
// between source and dest with blanks.
|
// fill in between source and dest with blanks.
|
||||||
for c in &mut line[source..destination] {
|
for c in &mut line[source..destination] {
|
||||||
c.reset(&cursor.template);
|
c.reset(&cursor.template);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ pub struct RegexSearch {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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> {
|
pub fn new(search: &str) -> Result<RegexSearch, RegexError> {
|
||||||
// Check case info for smart case
|
// Check case info for smart case
|
||||||
let has_uppercase = search.chars().any(|c| c.is_uppercase());
|
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;
|
let start_char = self.grid[point.line][point.col].c;
|
||||||
|
|
||||||
// Find the matching bracket we're looking for
|
// 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 {
|
if open == &start_char {
|
||||||
Some((true, *close))
|
Some((true, *close))
|
||||||
} else if close == &start_char {
|
} else if close == &start_char {
|
||||||
|
@ -336,7 +336,7 @@ impl<T> Term<T> {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Check the next cell
|
// 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
|
// Break if there are no more cells
|
||||||
let c = match cell {
|
let c = match cell {
|
||||||
|
@ -633,7 +633,7 @@ mod tests {
|
||||||
fn reverse_search_dead_recovery() {
|
fn reverse_search_dead_recovery() {
|
||||||
let mut term = mock_term("zooo lense");
|
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());
|
term.regex_search = Some(RegexSearch::new("zoo").unwrap());
|
||||||
let start = Point::new(0, Column(9));
|
let start = Point::new(0, Column(9));
|
||||||
let end = Point::new(0, Column(0));
|
let end = Point::new(0, Column(0));
|
||||||
|
|
Loading…
Reference in a new issue