diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs index cc5932a781deb100dd5b08be89bbb72e4b196a36..b843726a2e402f2b87822107c5aba3e85d348d2e 100644 --- a/crates/diagnostics/src/items.rs +++ b/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() diff --git a/crates/terminal/src/search.rs b/crates/terminal/src/search.rs new file mode 100644 index 0000000000000000000000000000000000000000..d9d7b076eb9ccb036cca67370222e159ba78f414 --- /dev/null +++ b/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>) -> Self { + Self { + matches: matches.into(), + index: 0, + } + } + + /// Create from regex matches on term visable part. + pub fn visible_regex_matches(term: &Term, dfas: &RegexSearch) -> Self { + let matches = visible_regex_match_iter(term, dfas).collect::>(); + 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, + regex: &'a RegexSearch, +) -> impl Iterator + '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) +} diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 6b9ec4f295d684bd726fa49103231f7534a2af72..3b40452ee6fe3ddc6aa1b716a5708eafc04b5b97 100644 --- a/crates/terminal/src/terminal.rs +++ b/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 String + Sync + Send + 'static>), Resize(TerminalSize), Clear, - Scroll(Scroll), + FocusNextMatch, + Scroll(AlacScroll), SetSelection(Option), UpdateSelection(Vector2F), Copy, @@ -172,8 +168,12 @@ impl From 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, 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, ) { 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::().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::().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(&mut self, cx: &mut ModelContext, f: F) -> T where - F: FnOnce(RenderableContent, char) -> T, + F: FnOnce(RenderableContent, char, Option) -> 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(); diff --git a/crates/terminal/src/terminal_element.rs b/crates/terminal/src/terminal_element.rs index 7cb3f032e4066ffdbfc9d8b7eb28dba941733323..2f6c0109273ab6661728f83100c3a6c53f5c32f9 100644 --- a/crates/terminal/src/terminal_element.rs +++ b/crates/terminal/src/terminal_element.rs @@ -43,7 +43,7 @@ use crate::{ pub struct LayoutState { cells: Vec, rects: Vec, - highlights: Vec, + selections: Vec, cursor: Option, 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)