diff --git a/src/main.rs b/src/main.rs index 296bae5..d68b2dc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -181,7 +181,6 @@ pub enum Message { TabNew, TermEvent(segmented_button::Entity, TermEvent), TermEventTx(mpsc::Sender<(segmented_button::Entity, TermEvent)>), - Todo(&'static str), ToggleContextPage(ContextPage), ShowAdvancedFontSettings(bool), WindowClose, @@ -806,7 +805,11 @@ impl Application for App { } Message::FindNext => { if !self.find_search_value.is_empty() { - //TODO + let entity = self.tab_model.active(); + if let Some(terminal) = self.tab_model.data::>(entity) { + let mut terminal = terminal.lock().unwrap(); + terminal.search(&self.find_search_value, true); + } } // Focus correct input @@ -814,7 +817,11 @@ impl Application for App { } Message::FindPrevious => { if !self.find_search_value.is_empty() { - //TODO + let entity = self.tab_model.active(); + if let Some(terminal) = self.tab_model.data::>(entity) { + let mut terminal = terminal.lock().unwrap(); + terminal.search(&self.find_search_value, false); + } } // Focus correct input @@ -1006,9 +1013,6 @@ impl Application for App { Message::TermEventTx(term_event_tx) => { self.term_event_tx_opt = Some(term_event_tx); } - Message::Todo(todo) => { - log::warn!("TODO: {}", todo); - } Message::ToggleContextPage(context_page) => { if self.context_page == context_page { self.core.window.show_context = !self.core.window.show_context; @@ -1121,10 +1125,12 @@ impl Application for App { widget::text_input::text_input(fl!("find-placeholder"), &self.find_search_value) .id(self.find_search_id.clone()) .on_input(Message::FindSearchValueChanged) + // This is inverted for ease of use, usually in terminals you want to search + // upwards, which is FindPrevious .on_submit(if self.modifiers.contains(Modifiers::SHIFT) { - Message::FindPrevious - } else { Message::FindNext + } else { + Message::FindPrevious }) .width(Length::Fixed(320.0)) .trailing_icon( diff --git a/src/terminal.rs b/src/terminal.rs index 87404c5..63a1595 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -2,12 +2,13 @@ use alacritty_terminal::{ event::{Event, EventListener, Notify, OnResize, WindowSize}, event_loop::{EventLoop, Msg, Notifier}, grid::Dimensions, - index::{Column, Line, Point, Side}, + index::{Column, Direction, Line, Point, Side}, selection::{Selection, SelectionType}, sync::FairMutex, term::{ cell::Flags, color::{self, Colors}, + search::{Match, RegexSearch}, viewport_to_point, Config, TermMode, }, tty::{self, Options}, @@ -135,6 +136,8 @@ pub struct Terminal { notifier: Notifier, pub context_menu: Option, pub needs_update: bool, + search_regex_opt: Option, + search_value: String, } impl Terminal { @@ -205,6 +208,8 @@ impl Terminal { notifier, context_menu: None, needs_update: true, + search_regex_opt: None, + search_value: String::new(), } } @@ -323,6 +328,89 @@ impl Terminal { } } + pub fn search(&mut self, value: &str, forwards: bool) { + //TODO: set max lines, run in thread? + { + let mut term = self.term.lock(); + + if self.search_value != value { + match RegexSearch::new(value) { + Ok(search_regex) => { + self.search_regex_opt = Some(search_regex); + self.search_value = value.to_string(); + term.selection = None; + } + Err(err) => { + log::warn!("failed to parse regex {:?}: {}", value, err); + return; + } + } + } + + let search_regex = match &mut self.search_regex_opt { + Some(some) => some, + None => return, + }; + + // Determine search origin + let search_origin = match term + .selection + .as_ref() + .and_then(|selection| selection.to_range(&term)) + { + Some(range) => { + if forwards { + range.end + } else { + range.start + } + } + None => { + let grid = term.grid(); + if forwards { + Point::new(Line(-(grid.history_size() as i32)), Column(0)) + } else { + Point::new( + Line(grid.screen_lines() as i32 - 1), + Column(grid.columns() - 1), + ) + } + } + }; + + // Find next search match + match term.search_next( + search_regex, + search_origin, + if forwards { + Direction::Right + } else { + Direction::Left + }, + if forwards { Side::Left } else { Side::Right }, + None, + ) { + Some(search_match) => { + // Scroll to match + if forwards { + term.scroll_to_point(*search_match.end()); + } else { + term.scroll_to_point(*search_match.start()); + } + + // Set selection to match + let mut selection = + Selection::new(SelectionType::Simple, *search_match.start(), Side::Left); + selection.update(*search_match.end(), Side::Right); + term.selection = Some(selection); + } + None => {} + } + } + + self.update(); + } + pub fn select_all(&mut self) { { let mut term = self.term.lock();