search stuff

Mikayla Maki created

Change summary

crates/diagnostics/src/items.rs         |   2 
crates/terminal/src/search.rs           |  78 +++++++++++++++++++++
crates/terminal/src/terminal.rs         | 100 ++++++++++++++------------
crates/terminal/src/terminal_element.rs |  15 ++-
4 files changed, 140 insertions(+), 55 deletions(-)

Detailed changes

crates/diagnostics/src/items.rs 🔗

@@ -179,7 +179,7 @@ impl View for DiagnosticIndicator {
         if in_progress {
             element.add_child(
                 Label::new(
-                    "checking…".into(),
+                    "Checking…".into(),
                     style.diagnostic_message.default.text.clone(),
                 )
                 .aligned()

crates/terminal/src/search.rs 🔗

@@ -0,0 +1,78 @@
+use std::{borrow::Cow, ops::Deref};
+
+use alacritty_terminal::{
+    grid::Dimensions,
+    index::{Column, Direction, Line, Point},
+    term::search::{Match, RegexIter, RegexSearch},
+    Term,
+};
+
+const MAX_SEARCH_LINES: usize = 100;
+
+///Header and impl fom alacritty/src/display/content.rs HintMatches
+#[derive(Default)]
+pub struct SearchMatches<'a> {
+    /// All visible matches.
+    matches: Cow<'a, [Match]>,
+
+    /// Index of the last match checked.
+    index: usize,
+}
+
+impl<'a> SearchMatches<'a> {
+    /// Create new renderable matches iterator..
+    fn new(matches: impl Into<Cow<'a, [Match]>>) -> Self {
+        Self {
+            matches: matches.into(),
+            index: 0,
+        }
+    }
+
+    /// Create from regex matches on term visable part.
+    pub fn visible_regex_matches<T>(term: &Term<T>, dfas: &RegexSearch) -> Self {
+        let matches = visible_regex_match_iter(term, dfas).collect::<Vec<_>>();
+        Self::new(matches)
+    }
+
+    /// Advance the regex tracker to the next point.
+    ///
+    /// This will return `true` if the point passed is part of a regex match.
+    fn advance(&mut self, point: Point) -> bool {
+        while let Some(bounds) = self.get(self.index) {
+            if bounds.start() > &point {
+                break;
+            } else if bounds.end() < &point {
+                self.index += 1;
+            } else {
+                return true;
+            }
+        }
+        false
+    }
+}
+
+impl<'a> Deref for SearchMatches<'a> {
+    type Target = [Match];
+
+    fn deref(&self) -> &Self::Target {
+        self.matches.deref()
+    }
+}
+
+/// Copied from alacritty/src/display/hint.rs
+/// Iterate over all visible regex matches.
+fn visible_regex_match_iter<'a, T>(
+    term: &'a Term<T>,
+    regex: &'a RegexSearch,
+) -> impl Iterator<Item = Match> + 'a {
+    let viewport_start = Line(-(term.grid().display_offset() as i32));
+    let viewport_end = viewport_start + term.bottommost_line();
+    let mut start = term.line_search_left(Point::new(viewport_start, Column(0)));
+    let mut end = term.line_search_right(Point::new(viewport_end, Column(0)));
+    start.line = start.line.max(viewport_start - MAX_SEARCH_LINES);
+    end.line = end.line.min(viewport_end + MAX_SEARCH_LINES);
+
+    RegexIter::new(start, end, Direction::Right, term, regex)
+        .skip_while(move |rm| rm.end().line < viewport_start)
+        .take_while(move |rm| rm.start().line <= viewport_end)
+}

crates/terminal/src/terminal.rs 🔗

@@ -1,5 +1,6 @@
 pub mod mappings;
 pub mod modal;
+pub mod search;
 pub mod terminal_container_view;
 pub mod terminal_element;
 pub mod terminal_view;
@@ -13,7 +14,7 @@ use alacritty_terminal::{
     index::{Direction, Point},
     selection::{Selection, SelectionType},
     sync::FairMutex,
-    term::{search::RegexSearch, RenderableContent, TermMode},
+    term::{color::Rgb, search::RegexSearch, RenderableContent, TermMode},
     tty::{self, setup_env},
     Term,
 };
@@ -81,18 +82,13 @@ pub enum Event {
     BlinkChanged,
 }
 
-#[derive(Clone, Debug)]
-enum Scroll {
-    AlacScroll(AlacScroll),
-    ToNextSearch,
-}
-
-#[derive(Clone, Debug)]
+#[derive(Clone)]
 enum InternalEvent {
-    TermEvent(AlacTermEvent),
+    ColorRequest(usize, Arc<dyn Fn(Rgb) -> String + Sync + Send + 'static>),
     Resize(TerminalSize),
     Clear,
-    Scroll(Scroll),
+    FocusNextMatch,
+    Scroll(AlacScroll),
     SetSelection(Option<Selection>),
     UpdateSelection(Vector2F),
     Copy,
@@ -172,8 +168,12 @@ impl From<TerminalSize> for WindowSize {
 }
 
 impl Dimensions for TerminalSize {
+    /// Note: this is supposed to be for the back buffer's length,
+    /// but we exclusively use it to resize the terminal, which does not
+    /// use this method. We still have to implement it for the trait though,
+    /// hence, this comment.
     fn total_lines(&self) -> usize {
-        self.screen_lines() //TODO: Check that this is fine. This is supposed to be for the back buffer...
+        self.screen_lines()
     }
 
     fn screen_lines(&self) -> usize {
@@ -378,7 +378,6 @@ impl TerminalBuilder {
             cur_size: initial_size,
             last_mouse: None,
             last_offset: 0,
-            has_selection: false,
             searcher: None,
         };
 
@@ -451,7 +450,6 @@ pub struct Terminal {
     last_mode: TermMode,
     last_offset: usize,
     last_mouse: Option<(Point, Direction)>,
-    has_selection: bool,
     searcher: Option<(Option<RegexSearch>, Point)>,
 }
 
@@ -492,9 +490,11 @@ impl Terminal {
                 cx.emit(Event::Wakeup);
                 cx.notify();
             }
-            AlacTermEvent::ColorRequest(_, _) => self
-                .events
-                .push_back(InternalEvent::TermEvent(event.clone())),
+            AlacTermEvent::ColorRequest(idx, fun_ptr) => {
+                self.events
+                    .push_back(InternalEvent::ColorRequest(*idx, fun_ptr.clone()));
+                cx.notify(); //Immediately schedule a render to respond to the color request
+            }
         }
     }
 
@@ -506,14 +506,12 @@ impl Terminal {
         cx: &mut ModelContext<Self>,
     ) {
         match event {
-            InternalEvent::TermEvent(term_event) => {
-                if let AlacTermEvent::ColorRequest(index, format) = term_event {
-                    let color = term.colors()[*index].unwrap_or_else(|| {
-                        let term_style = &cx.global::<Settings>().theme.terminal;
-                        to_alac_rgb(get_color_at_index(index, &term_style.colors))
-                    });
-                    self.write_to_pty(format(color))
-                }
+            InternalEvent::ColorRequest(index, format) => {
+                let color = term.colors()[*index].unwrap_or_else(|| {
+                    let term_style = &cx.global::<Settings>().theme.terminal;
+                    to_alac_rgb(get_color_at_index(index, &term_style.colors))
+                });
+                self.write_to_pty(format(color))
             }
             InternalEvent::Resize(new_size) => {
                 self.cur_size = *new_size;
@@ -526,17 +524,24 @@ impl Terminal {
                 self.write_to_pty("\x0c".to_string());
                 term.clear_screen(ClearMode::Saved);
             }
-            InternalEvent::Scroll(Scroll::AlacScroll(scroll)) => {
+            InternalEvent::Scroll(scroll) => {
                 term.scroll_display(*scroll);
             }
-            InternalEvent::Scroll(Scroll::ToNextSearch) => {
+            InternalEvent::FocusNextMatch => {
                 if let Some((Some(searcher), origin)) = &self.searcher {
                     match term.search_next(searcher, *origin, SEARCH_FORWARD, Direction::Left, None)
                     {
                         Some(regex_match) => {
-                            //Jump to spot
+                            term.scroll_to_point(*regex_match.start());
+
+                            //Focus is done with selections in zed
+                            let focus = make_selection(*regex_match.start(), *regex_match.end());
+                            term.selection = Some(focus);
+                        }
+                        None => {
+                            //Clear focused match
+                            term.selection = None;
                         }
-                        None => { /*reset state*/ }
                     }
                 }
             }
@@ -560,7 +565,6 @@ impl Terminal {
     }
 
     fn begin_select(&mut self, sel: Selection) {
-        self.has_selection = true;
         self.events
             .push_back(InternalEvent::SetSelection(Some(sel)));
     }
@@ -571,35 +575,30 @@ impl Terminal {
     }
 
     fn end_select(&mut self) {
-        self.has_selection = false;
         self.events.push_back(InternalEvent::SetSelection(None));
     }
 
     fn scroll(&mut self, scroll: AlacScroll) {
-        self.events
-            .push_back(InternalEvent::Scroll(Scroll::AlacScroll(scroll)));
+        self.events.push_back(InternalEvent::Scroll(scroll));
     }
 
-    fn scroll_to_next_search(&mut self) {
-        self.events
-            .push_back(InternalEvent::Scroll(Scroll::ToNextSearch));
+    fn focus_next_match(&mut self) {
+        self.events.push_back(InternalEvent::FocusNextMatch);
     }
 
     pub fn search(&mut self, search: &str) {
         let new_searcher = RegexSearch::new(search).ok();
         self.searcher = match (new_searcher, &self.searcher) {
-            //Existing search, carry over origin
-            (Some(new_searcher), Some((_, origin))) => Some((Some(new_searcher), *origin)),
-            //No existing search, start a new one
-            (Some(new_searcher), None) => Some((Some(new_searcher), self.viewport_origin())),
-            //Error creating a new search, carry over origin
-            (None, Some((_, origin))) => Some((None, *origin)),
             //Nothing to do :(
             (None, None) => None,
+            //No existing search, start a new one
+            (Some(new_searcher), None) => Some((Some(new_searcher), self.viewport_origin())),
+            //Existing search, carry over origin
+            (new_searcher, Some((_, origin))) => Some((new_searcher, *origin)),
         };
 
         if let Some((Some(_), _)) = self.searcher {
-            self.scroll_to_next_search();
+            self.focus_next_match();
         }
     }
 
@@ -658,7 +657,7 @@ impl Terminal {
 
     pub fn render_lock<F, T>(&mut self, cx: &mut ModelContext<Self>, f: F) -> T
     where
-        F: FnOnce(RenderableContent, char) -> T,
+        F: FnOnce(RenderableContent, char, Option<RegexSearch>) -> T,
     {
         let m = self.term.clone(); //Arc clone
         let mut term = m.lock();
@@ -672,14 +671,15 @@ impl Terminal {
 
         let content = term.renderable_content();
 
-        // term.line_search_right(point)
-        // term.search_next(dfas, origin, direction, side, max_lines)
-
         self.last_offset = content.display_offset;
 
         let cursor_text = term.grid()[content.cursor.point].c;
 
-        f(content, cursor_text)
+        f(
+            content,
+            cursor_text,
+            self.searcher.as_ref().and_then(|s| s.0.clone()),
+        )
     }
 
     pub fn focus_in(&self) {
@@ -854,6 +854,12 @@ impl Terminal {
     }
 }
 
+fn make_selection(from: Point, to: Point) -> Selection {
+    let mut focus = Selection::new(SelectionType::Simple, from, Direction::Left);
+    focus.update(to, Direction::Right);
+    focus
+}
+
 impl Drop for Terminal {
     fn drop(&mut self) {
         self.pty_tx.0.send(Msg::Shutdown).ok();

crates/terminal/src/terminal_element.rs 🔗

@@ -43,7 +43,7 @@ use crate::{
 pub struct LayoutState {
     cells: Vec<LayoutCell>,
     rects: Vec<LayoutRect>,
-    highlights: Vec<RelativeHighlightedRange>,
+    selections: Vec<RelativeHighlightedRange>,
     cursor: Option<Cursor>,
     background_color: Color,
     selection_color: Color,
@@ -624,13 +624,13 @@ impl Element for TerminalElement {
             terminal_theme.colors.background
         };
 
-        let (cells, selection, cursor, display_offset, cursor_text, mode) = self
+        let (cells, selection, cursor, display_offset, cursor_text, searcher, mode) = self
             .terminal
             .upgrade(cx)
             .unwrap()
             .update(cx.app, |terminal, mcx| {
                 terminal.set_size(dimensions);
-                terminal.render_lock(mcx, |content, cursor_text| {
+                terminal.render_lock(mcx, |content, cursor_text, searcher| {
                     let mut cells = vec![];
                     cells.extend(
                         content
@@ -653,12 +653,13 @@ impl Element for TerminalElement {
                         content.cursor,
                         content.display_offset,
                         cursor_text,
+                        searcher,
                         content.mode,
                     )
                 })
             });
 
-        let (cells, rects, highlights) = TerminalElement::layout_grid(
+        let (cells, rects, selections) = TerminalElement::layout_grid(
             cells,
             &text_style,
             &terminal_theme,
@@ -731,7 +732,7 @@ impl Element for TerminalElement {
                 selection_color,
                 size: dimensions,
                 rects,
-                highlights,
+                selections,
                 mode,
             },
         )
@@ -769,13 +770,13 @@ impl Element for TerminalElement {
 
             //Draw Selection
             cx.paint_layer(clip_bounds, |cx| {
-                let start_y = layout.highlights.get(0).map(|highlight| {
+                let start_y = layout.selections.get(0).map(|highlight| {
                     origin.y() + highlight.line_index as f32 * layout.size.line_height
                 });
 
                 if let Some(y) = start_y {
                     let range_lines = layout
-                        .highlights
+                        .selections
                         .iter()
                         .map(|relative_highlight| {
                             relative_highlight.to_highlighted_range_line(origin, layout)