@@ -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)
+}
@@ -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();
@@ -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)