@@ -1,78 +0,0 @@
-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,6 +1,5 @@
pub mod mappings;
pub mod modal;
-pub mod search;
pub mod terminal_container_view;
pub mod terminal_element;
pub mod terminal_view;
@@ -11,10 +10,14 @@ use alacritty_terminal::{
event::{Event as AlacTermEvent, EventListener, Notify, WindowSize},
event_loop::{EventLoop, Msg, Notifier},
grid::{Dimensions, Scroll as AlacScroll},
- index::{Direction, Point},
+ index::{Column, Direction, Line, Point},
selection::{Selection, SelectionType},
sync::FairMutex,
- term::{color::Rgb, search::RegexSearch, RenderableContent, TermMode},
+ term::{
+ color::Rgb,
+ search::{Match, RegexIter, RegexSearch},
+ RenderableContent, TermMode,
+ },
tty::{self, setup_env},
Term,
};
@@ -28,6 +31,7 @@ use mappings::mouse::{
alt_scroll, mouse_button_report, mouse_moved_report, mouse_point, mouse_side, scroll_report,
};
use modal::deploy_modal;
+
use settings::{AlternateScroll, Settings, Shell, TerminalBlink};
use std::{
collections::{HashMap, VecDeque},
@@ -66,7 +70,7 @@ pub fn init(cx: &mut MutableAppContext) {
const ALACRITTY_SCROLL_MULTIPLIER: f32 = 3.;
// const ALACRITTY_SEARCH_LINE_LIMIT: usize = 1000;
const SEARCH_FORWARD: Direction = Direction::Left;
-
+const MAX_SEARCH_LINES: usize = 100;
const DEBUG_TERMINAL_WIDTH: f32 = 500.;
const DEBUG_TERMINAL_HEIGHT: f32 = 30.;
const DEBUG_CELL_WIDTH: f32 = 5.;
@@ -528,9 +532,17 @@ impl Terminal {
term.scroll_display(*scroll);
}
InternalEvent::FocusNextMatch => {
- if let Some((Some(searcher), origin)) = &self.searcher {
- match term.search_next(searcher, *origin, SEARCH_FORWARD, Direction::Left, None)
- {
+ if let Some((Some(searcher), _origin)) = &self.searcher {
+ match term.search_next(
+ searcher,
+ Point {
+ line: Line(0),
+ column: Column(0),
+ },
+ SEARCH_FORWARD,
+ Direction::Left,
+ None,
+ ) {
Some(regex_match) => {
term.scroll_to_point(*regex_match.start());
@@ -657,7 +669,7 @@ impl Terminal {
pub fn render_lock<F, T>(&mut self, cx: &mut ModelContext<Self>, f: F) -> T
where
- F: FnOnce(RenderableContent, char, Option<RegexSearch>) -> T,
+ F: FnOnce(RenderableContent, char, Vec<Match>) -> T,
{
let m = self.term.clone(); //Arc clone
let mut term = m.lock();
@@ -675,11 +687,12 @@ impl Terminal {
let cursor_text = term.grid()[content.cursor.point].c;
- f(
- content,
- cursor_text,
- self.searcher.as_ref().and_then(|s| s.0.clone()),
- )
+ let mut matches = vec![];
+ if let Some((Some(r), _)) = &self.searcher {
+ matches.extend(make_search_matches(&term, &r));
+ }
+
+ f(content, cursor_text, matches)
}
pub fn focus_in(&self) {
@@ -854,12 +867,6 @@ 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();
@@ -870,6 +877,30 @@ impl Entity for Terminal {
type Event = Event;
}
+fn make_selection(from: Point, to: Point) -> Selection {
+ let mut focus = Selection::new(SelectionType::Simple, from, Direction::Left);
+ focus.update(to, Direction::Right);
+ focus
+}
+
+/// Copied from alacritty/src/display/hint.rs HintMatches::visible_regex_matches()
+/// Iterate over all visible regex matches.
+fn make_search_matches<'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)
+}
+
#[cfg(test)]
mod tests {
pub mod terminal_test_context;
@@ -2,7 +2,6 @@ use alacritty_terminal::{
ansi::{Color as AnsiColor, Color::Named, CursorShape as AlacCursorShape, NamedColor},
grid::Dimensions,
index::Point,
- selection::SelectionRange,
term::{
cell::{Cell, Flags},
TermMode,
@@ -27,7 +26,7 @@ use settings::Settings;
use theme::TerminalStyle;
use util::ResultExt;
-use std::fmt::Debug;
+use std::{fmt::Debug, ops::RangeInclusive};
use std::{
mem,
ops::{Deref, Range},
@@ -43,12 +42,12 @@ use crate::{
pub struct LayoutState {
cells: Vec<LayoutCell>,
rects: Vec<LayoutRect>,
- selections: Vec<RelativeHighlightedRange>,
+ relative_highlighted_ranges: Vec<(RangeInclusive<Point>, Color)>,
cursor: Option<Cursor>,
background_color: Color,
- selection_color: Color,
size: TerminalSize,
mode: TermMode,
+ display_offset: usize,
}
#[derive(Debug)]
@@ -166,30 +165,6 @@ impl LayoutRect {
}
}
-#[derive(Clone, Debug, Default)]
-struct RelativeHighlightedRange {
- line_index: usize,
- range: Range<usize>,
-}
-
-impl RelativeHighlightedRange {
- fn new(line_index: usize, range: Range<usize>) -> Self {
- RelativeHighlightedRange { line_index, range }
- }
-
- fn to_highlighted_range_line(
- &self,
- origin: Vector2F,
- layout: &LayoutState,
- ) -> HighlightedRangeLine {
- let start_x = origin.x() + self.range.start as f32 * layout.size.cell_width;
- let end_x =
- origin.x() + self.range.end as f32 * layout.size.cell_width + layout.size.cell_width;
-
- HighlightedRangeLine { start_x, end_x }
- }
-}
-
///The GPUI element that paints the terminal.
///We need to keep a reference to the view for mouse events, do we need it for any other terminal stuff, or can we move that to connection?
pub struct TerminalElement {
@@ -217,6 +192,8 @@ impl TerminalElement {
}
}
+ //Vec<Range<Point>> -> Clip out the parts of the ranges
+
fn layout_grid(
grid: Vec<IndexedCell>,
text_style: &TextStyle,
@@ -224,41 +201,22 @@ impl TerminalElement {
text_layout_cache: &TextLayoutCache,
font_cache: &FontCache,
modal: bool,
- selection_range: Option<SelectionRange>,
- ) -> (
- Vec<LayoutCell>,
- Vec<LayoutRect>,
- Vec<RelativeHighlightedRange>,
- ) {
+ ) -> (Vec<LayoutCell>, Vec<LayoutRect>) {
let mut cells = vec![];
let mut rects = vec![];
- let mut highlight_ranges = vec![];
let mut cur_rect: Option<LayoutRect> = None;
let mut cur_alac_color = None;
- let mut highlighted_range = None;
let linegroups = grid.into_iter().group_by(|i| i.point.line);
for (line_index, (_, line)) in linegroups.into_iter().enumerate() {
- for (x_index, cell) in line.enumerate() {
+ for cell in line {
let mut fg = cell.fg;
let mut bg = cell.bg;
if cell.flags.contains(Flags::INVERSE) {
mem::swap(&mut fg, &mut bg);
}
- //Increase selection range
- {
- if selection_range
- .map(|range| range.contains(cell.point))
- .unwrap_or(false)
- {
- let mut range = highlighted_range.take().unwrap_or(x_index..x_index);
- range.end = range.end.max(x_index);
- highlighted_range = Some(range);
- }
- }
-
//Expand background rect range
{
if matches!(bg, Named(NamedColor::Background)) {
@@ -324,18 +282,11 @@ impl TerminalElement {
};
}
- if highlighted_range.is_some() {
- highlight_ranges.push(RelativeHighlightedRange::new(
- line_index,
- highlighted_range.take().unwrap(),
- ))
- }
-
if cur_rect.is_some() {
rects.push(cur_rect.take().unwrap());
}
}
- (cells, rects, highlight_ranges)
+ (cells, rects)
}
// Compute the cursor position and expected block width, may return a zero width if x_for_index returns
@@ -612,6 +563,7 @@ impl Element for TerminalElement {
let terminal_theme = settings.theme.terminal.clone(); //TODO: Try to minimize this clone.
let text_style = TerminalElement::make_text_style(font_cache, settings);
let selection_color = settings.theme.editor.selection.selection;
+ let match_color = settings.theme.search.match_background;
let dimensions = {
let line_height = font_cache.line_height(text_style.font_size);
let cell_width = font_cache.em_advance(text_style.font_id, text_style.font_size);
@@ -624,13 +576,13 @@ impl Element for TerminalElement {
terminal_theme.colors.background
};
- let (cells, selection, cursor, display_offset, cursor_text, searcher, mode) = self
+ let (cells, selection, cursor, display_offset, cursor_text, search_matches, mode) = self
.terminal
.upgrade(cx)
.unwrap()
- .update(cx.app, |terminal, mcx| {
+ .update(cx.app, |terminal, cx| {
terminal.set_size(dimensions);
- terminal.render_lock(mcx, |content, cursor_text, searcher| {
+ terminal.render_lock(cx, |content, cursor_text, search_matches| {
let mut cells = vec![];
cells.extend(
content
@@ -653,20 +605,30 @@ impl Element for TerminalElement {
content.cursor,
content.display_offset,
cursor_text,
- searcher,
+ search_matches.clone(),
content.mode,
)
})
});
- let (cells, rects, selections) = TerminalElement::layout_grid(
+ // searches, highlights to a single range representations
+ let mut relative_highlighted_ranges = Vec::new();
+ if let Some(selection) = selection {
+ relative_highlighted_ranges.push((selection.start..=selection.end, selection_color));
+ }
+ for search_match in search_matches {
+ relative_highlighted_ranges.push((search_match, match_color))
+ }
+
+ // then have that representation be converted to the appropriate highlight data structure
+
+ let (cells, rects) = TerminalElement::layout_grid(
cells,
&text_style,
&terminal_theme,
cx.text_layout_cache,
cx.font_cache(),
self.modal,
- selection,
);
//Layout cursor. Rectangle is used for IME, so we should lay it out even
@@ -729,11 +691,11 @@ impl Element for TerminalElement {
cells,
cursor,
background_color,
- selection_color,
size: dimensions,
rects,
- selections,
+ relative_highlighted_ranges,
mode,
+ display_offset,
},
)
}
@@ -768,30 +730,23 @@ impl Element for TerminalElement {
}
});
- //Draw Selection
+ //Draw Highlighted Backgrounds
cx.paint_layer(clip_bounds, |cx| {
- 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
- .selections
- .iter()
- .map(|relative_highlight| {
- relative_highlight.to_highlighted_range_line(origin, layout)
- })
- .collect::<Vec<HighlightedRangeLine>>();
-
- let hr = HighlightedRange {
- start_y: y, //Need to change this
- line_height: layout.size.line_height,
- lines: range_lines,
- color: layout.selection_color,
- //Copied from editor. TODO: move to theme or something
- corner_radius: 0.15 * layout.size.line_height,
- };
- hr.paint(bounds, cx.scene);
+ for (relative_highlighted_range, color) in layout.relative_highlighted_ranges.iter()
+ {
+ if let Some((start_y, highlighted_range_lines)) =
+ to_highlighted_range_lines(relative_highlighted_range, layout, origin)
+ {
+ let hr = HighlightedRange {
+ start_y, //Need to change this
+ line_height: layout.size.line_height,
+ lines: highlighted_range_lines,
+ color: color.clone(),
+ //Copied from editor. TODO: move to theme or something
+ corner_radius: 0.15 * layout.size.line_height,
+ };
+ hr.paint(bounds, cx.scene);
+ }
}
});
@@ -894,3 +849,65 @@ impl Element for TerminalElement {
Some(layout.cursor.as_ref()?.bounding_rect(origin))
}
}
+
+fn to_highlighted_range_lines(
+ range: &RangeInclusive<Point>,
+ layout: &LayoutState,
+ origin: Vector2F,
+) -> Option<(f32, Vec<HighlightedRangeLine>)> {
+ // Step 1. Normalize the points to be viewport relative.
+ //When display_offset = 1, here's how the grid is arranged:
+ //--- Viewport top
+ //-2,0 -2,1...
+ //-1,0 -1,1...
+ //--------- Terminal Top
+ // 0,0 0,1...
+ // 1,0 1,1...
+ //--- Viewport Bottom
+ // 2,0 2,1...
+ //--------- Terminal Bottom
+
+ // Normalize to viewport relative, from terminal relative.
+ // lines are i32s, which are negative above the top left corner of the terminal
+ // If the user has scrolled, we use the display_offset to tell us which offset
+ // of the grid data we should be looking at. But for the rendering step, we don't
+ // want negatives. We want things relative to the 'viewport' (the area of the grid
+ // which is currently shown according to the display offset)
+ let unclamped_start = Point::new(
+ range.start().line + layout.display_offset,
+ range.start().column,
+ );
+ let unclamped_end = Point::new(range.end().line + layout.display_offset, range.end().column);
+
+ // Step 2. Clamp range to viewport, and return None if it doesn't overlap
+ if unclamped_end.line.0 < 0 || unclamped_start.line.0 > layout.size.num_lines() as i32 {
+ return None;
+ }
+
+ let clamped_start_line = unclamped_start.line.0.max(0) as usize;
+ let clamped_end_line = unclamped_end.line.0.min(layout.size.num_lines() as i32) as usize;
+ //Convert the start of the range to pixels
+ let start_y = origin.y() + clamped_start_line as f32 * layout.size.line_height;
+
+ // Step 3. Expand ranges that cross lines into a collection of single-line ranges.
+ // (also convert to pixels)
+ let mut highlighted_range_lines = Vec::new();
+ for line in clamped_start_line..=clamped_end_line {
+ let mut line_start = 0;
+ let mut line_end = layout.size.columns();
+
+ if line == clamped_start_line {
+ line_start = unclamped_start.column.0 as usize;
+ }
+ if line == clamped_end_line {
+ line_end = unclamped_end.column.0 as usize + 1; //+1 for inclusive
+ }
+
+ highlighted_range_lines.push(HighlightedRangeLine {
+ start_x: origin.x() + line_start as f32 * layout.size.cell_width,
+ end_x: origin.x() + line_end as f32 * layout.size.cell_width,
+ });
+ }
+
+ Some((start_y, highlighted_range_lines))
+}