aboutsummaryrefslogtreecommitdiff
path: root/alacritty/src/display/content.rs
diff options
context:
space:
mode:
Diffstat (limited to 'alacritty/src/display/content.rs')
-rw-r--r--alacritty/src/display/content.rs147
1 files changed, 62 insertions, 85 deletions
diff --git a/alacritty/src/display/content.rs b/alacritty/src/display/content.rs
index 77571d94..478982bb 100644
--- a/alacritty/src/display/content.rs
+++ b/alacritty/src/display/content.rs
@@ -1,22 +1,21 @@
use std::borrow::Cow;
-use std::cmp::{max, min};
-use std::mem;
-use std::ops::{Deref, DerefMut, RangeInclusive};
+use std::ops::Deref;
+use std::{cmp, mem};
use alacritty_terminal::ansi::{Color, CursorShape, NamedColor};
use alacritty_terminal::event::EventListener;
-use alacritty_terminal::grid::{Dimensions, Indexed};
-use alacritty_terminal::index::{Column, Direction, Line, Point};
+use alacritty_terminal::grid::Indexed;
+use alacritty_terminal::index::{Column, Line, Point};
use alacritty_terminal::selection::SelectionRange;
-use alacritty_terminal::term::cell::{Cell, Flags};
+use alacritty_terminal::term::cell::{Cell, Flags, Hyperlink};
use alacritty_terminal::term::color::{CellRgb, Rgb};
-use alacritty_terminal::term::search::{Match, RegexIter, RegexSearch};
+use alacritty_terminal::term::search::{Match, RegexSearch};
use alacritty_terminal::term::{self, RenderableContent as TerminalContent, Term, TermMode};
use crate::config::UiConfig;
use crate::display::color::{List, DIM_FACTOR};
-use crate::display::hint::HintState;
-use crate::display::{Display, MAX_SEARCH_LINES};
+use crate::display::hint::{self, HintState};
+use crate::display::Display;
use crate::event::SearchState;
/// Minimum contrast between a fixed cursor color and the cell's background.
@@ -30,7 +29,7 @@ pub struct RenderableContent<'a> {
cursor: RenderableCursor,
cursor_shape: CursorShape,
cursor_point: Point<usize>,
- search: Option<Regex<'a>>,
+ search: Option<HintMatches<'a>>,
hint: Option<Hint<'a>>,
config: &'a UiConfig,
colors: &'a List,
@@ -44,7 +43,7 @@ impl<'a> RenderableContent<'a> {
term: &'a Term<T>,
search_state: &'a SearchState,
) -> Self {
- let search = search_state.dfas().map(|dfas| Regex::new(term, dfas));
+ let search = search_state.dfas().map(|dfas| HintMatches::visible_regex_matches(term, dfas));
let focused_match = search_state.focused_match();
let terminal_content = term.renderable_content();
@@ -181,13 +180,21 @@ impl<'a> Iterator for RenderableContent<'a> {
#[derive(Clone, Debug)]
pub struct RenderableCell {
pub character: char,
- pub zerowidth: Option<Vec<char>>,
pub point: Point<usize>,
pub fg: Rgb,
pub bg: Rgb,
pub bg_alpha: f32,
pub underline: Rgb,
pub flags: Flags,
+ pub extra: Option<Box<RenderableCellExtra>>,
+}
+
+/// Extra storage with rarely present fields for [`RenderableCell`], to reduce the cell size we
+/// pass around.
+#[derive(Clone, Debug)]
+pub struct RenderableCellExtra {
+ pub zerowidth: Option<Vec<char>>,
+ pub hyperlink: Option<Hyperlink>,
}
impl RenderableCell {
@@ -257,23 +264,24 @@ impl RenderableCell {
.underline_color()
.map_or(fg, |underline| Self::compute_fg_rgb(content, underline, flags));
- RenderableCell {
- zerowidth: cell.zerowidth().map(|zerowidth| zerowidth.to_vec()),
- flags,
- character,
- bg_alpha,
- point,
- fg,
- bg,
- underline,
- }
+ let zerowidth = cell.zerowidth();
+ let hyperlink = cell.hyperlink();
+
+ let extra = (zerowidth.is_some() || hyperlink.is_some()).then(|| {
+ Box::new(RenderableCellExtra {
+ zerowidth: zerowidth.map(|zerowidth| zerowidth.to_vec()),
+ hyperlink,
+ })
+ });
+
+ RenderableCell { flags, character, bg_alpha, point, fg, bg, underline, extra }
}
/// Check if cell contains any renderable content.
fn is_empty(&self) -> bool {
self.bg_alpha == 0.
&& self.character == ' '
- && self.zerowidth.is_none()
+ && self.extra.is_none()
&& !self.flags.intersects(Flags::ALL_UNDERLINES | Flags::STRIKEOUT)
}
@@ -406,7 +414,7 @@ impl RenderableCursor {
/// Regex hints for keyboard shortcuts.
struct Hint<'a> {
/// Hint matches and position.
- regex: Regex<'a>,
+ matches: HintMatches<'a>,
/// Last match checked against current cell position.
labels: &'a Vec<Vec<char>>,
@@ -421,16 +429,15 @@ impl<'a> Hint<'a> {
/// The tuple's [`bool`] will be `true` when the character is the first for this hint.
fn advance(&mut self, viewport_start: Point, point: Point) -> Option<(char, bool)> {
// Check if we're within a match at all.
- if !self.regex.advance(point) {
+ if !self.matches.advance(point) {
return None;
}
// Match starting position on this line; linebreaks interrupt the hint labels.
let start = self
- .regex
.matches
- .get(self.regex.index)
- .map(|regex_match| max(*regex_match.start(), viewport_start))
+ .get(self.matches.index)
+ .map(|bounds| cmp::max(*bounds.start(), viewport_start))
.filter(|start| start.line == point.line)?;
// Position within the hint label.
@@ -438,85 +445,47 @@ impl<'a> Hint<'a> {
let is_first = label_position == 0;
// Hint label character.
- self.labels[self.regex.index].get(label_position).copied().map(|c| (c, is_first))
+ self.labels[self.matches.index].get(label_position).copied().map(|c| (c, is_first))
}
}
impl<'a> From<&'a HintState> for Hint<'a> {
fn from(hint_state: &'a HintState) -> Self {
- let regex = Regex { matches: Cow::Borrowed(hint_state.matches()), index: 0 };
- Self { labels: hint_state.labels(), regex }
- }
-}
-
-/// Wrapper for finding visible regex matches.
-#[derive(Default, Clone)]
-pub struct RegexMatches(pub Vec<RangeInclusive<Point>>);
-
-impl RegexMatches {
- /// Find all visible matches.
- pub fn new<T>(term: &Term<T>, dfas: &RegexSearch) -> Self {
- let viewport_start = Line(-(term.grid().display_offset() as i32));
- let viewport_end = viewport_start + term.bottommost_line();
-
- // Compute start of the first and end of the last line.
- let start_point = Point::new(viewport_start, Column(0));
- let mut start = term.line_search_left(start_point);
- let end_point = Point::new(viewport_end, term.last_column());
- let mut end = term.line_search_right(end_point);
-
- // Set upper bound on search before/after the viewport to prevent excessive blocking.
- start.line = max(start.line, viewport_start - MAX_SEARCH_LINES);
- end.line = min(end.line, viewport_end + MAX_SEARCH_LINES);
-
- // Create an iterater for the current regex search for all visible matches.
- let iter = RegexIter::new(start, end, Direction::Right, term, dfas)
- .skip_while(move |rm| rm.end().line < viewport_start)
- .take_while(move |rm| rm.start().line <= viewport_end);
-
- Self(iter.collect())
- }
-}
-
-impl Deref for RegexMatches {
- type Target = Vec<RangeInclusive<Point>>;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-impl DerefMut for RegexMatches {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.0
+ let matches = HintMatches::new(hint_state.matches());
+ Self { labels: hint_state.labels(), matches }
}
}
-/// Visible regex match tracking.
+/// Visible hint match tracking.
#[derive(Default)]
-struct Regex<'a> {
+struct HintMatches<'a> {
/// All visible matches.
- matches: Cow<'a, RegexMatches>,
+ matches: Cow<'a, [Match]>,
/// Index of the last match checked.
index: usize,
}
-impl<'a> Regex<'a> {
- /// Create a new renderable regex iterator.
- fn new<T>(term: &Term<T>, dfas: &RegexSearch) -> Self {
- let matches = Cow::Owned(RegexMatches::new(term, dfas));
- Self { index: 0, matches }
+impl<'a> HintMatches<'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.
+ fn visible_regex_matches<T>(term: &Term<T>, dfas: &RegexSearch) -> Self {
+ let matches = hint::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(regex_match) = self.matches.get(self.index) {
- if regex_match.start() > &point {
+ while let Some(bounds) = self.get(self.index) {
+ if bounds.start() > &point {
break;
- } else if regex_match.end() < &point {
+ } else if bounds.end() < &point {
self.index += 1;
} else {
return true;
@@ -525,3 +494,11 @@ impl<'a> Regex<'a> {
false
}
}
+
+impl<'a> Deref for HintMatches<'a> {
+ type Target = [Match];
+
+ fn deref(&self) -> &Self::Target {
+ self.matches.deref()
+ }
+}