Skip to content

Commit

Permalink
Apply post processing to hyperlink. So less regexs with unlikely trai…
Browse files Browse the repository at this point in the history
…ling characters are matches
  • Loading branch information
leb-kuchen authored and jackpot51 committed Dec 22, 2024
1 parent ff42dd2 commit 75e1082
Showing 1 changed file with 162 additions and 3 deletions.
165 changes: 162 additions & 3 deletions src/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -701,9 +701,15 @@ impl Terminal {
}
term.reset_damage();

let regex_match_iter = visible_regex_match_iter(&term, &mut self.url_regex_search);
self.regex_matches.clear();
self.regex_matches.extend(regex_match_iter);
{
let mut regex_matches: Vec<_> =
visible_regex_match_iter(&term, &mut self.url_regex_search).collect();
self.regex_matches
.extend(regex_matches.drain(..).flat_map(|rm| -> Vec<_> {
HintPostProcessor::new(&term, &mut self.url_regex_search, rm).collect()
}));
}

let grid = term.grid();
for indexed in grid.display_iter() {
Expand Down Expand Up @@ -962,7 +968,6 @@ impl Terminal {
}
}
}

/// Iterate over all visible regex matches.
/// This includes the screen +- 100 lines (MAX_SEARCH_LINES).
/// display/hint.rs
Expand All @@ -981,6 +986,160 @@ pub fn visible_regex_match_iter<'a, T>(
.skip_while(move |rm| rm.end().line < viewport_start)
.take_while(move |rm| rm.start().line <= viewport_end)
}
/** Copy of <https://github.com/alacritty/alacritty/blob/4a7728bf7fac06a35f27f6c4f31e0d9214e5152b/alacritty/src/display/hint.rs#L433C1-L572C1> */
/// Iterator over all post-processed matches inside an existing hint match.
struct HintPostProcessor<'a, T> {
/// Regex search DFAs.
regex: &'a mut RegexSearch,

/// Terminal reference.
term: &'a Term<T>,

/// Next hint match in the iterator.
next_match: Option<alacritty_terminal::term::search::Match>,

/// Start point for the next search.
start: Point,

/// End point for the hint match iterator.
end: Point,
}

impl<'a, T> HintPostProcessor<'a, T> {
/// Create a new iterator for an unprocessed match.
fn new(
term: &'a Term<T>,
regex: &'a mut RegexSearch,
regex_match: alacritty_terminal::term::search::Match,
) -> Self {
let mut post_processor = Self {
next_match: None,
start: *regex_match.start(),
end: *regex_match.end(),
term,
regex,
};

// Post-process the first hint match.
post_processor.next_processed_match(regex_match);

post_processor
}

/// Apply some hint post processing heuristics.
///
/// This will check the end of the hint and make it shorter if certain characters are determined
/// to be unlikely to be intentionally part of the hint.
///
/// This is most useful for identifying URLs appropriately.
fn hint_post_processing(
&self,
regex_match: &alacritty_terminal::term::search::Match,
) -> Option<alacritty_terminal::term::search::Match> {
let mut iter = self.term.grid().iter_from(*regex_match.start());

let mut c = iter.cell().c;

// Truncate uneven number of brackets.
let end = *regex_match.end();
let mut open_parents = 0;
let mut open_brackets = 0;
loop {
match c {
'(' => open_parents += 1,
'[' => open_brackets += 1,
')' => {
if open_parents == 0 {
alacritty_terminal::grid::BidirectionalIterator::prev(&mut iter);
break;
} else {
open_parents -= 1;
}
}
']' => {
if open_brackets == 0 {
alacritty_terminal::grid::BidirectionalIterator::prev(&mut iter);
break;
} else {
open_brackets -= 1;
}
}
_ => (),
}

if iter.point() == end {
break;
}

match iter.next() {
Some(indexed) => c = indexed.cell.c,
None => break,
}
}

// Truncate trailing characters which are likely to be delimiters.
let start = *regex_match.start();
while iter.point() != start {
if !matches!(c, '.' | ',' | ':' | ';' | '?' | '!' | '(' | '[' | '\'') {
break;
}

match alacritty_terminal::grid::BidirectionalIterator::prev(&mut iter) {
Some(indexed) => c = indexed.cell.c,
None => break,
}
}

if start > iter.point() {
None
} else {
Some(start..=iter.point())
}
}

/// Loop over submatches until a non-empty post-processed match is found.
fn next_processed_match(&mut self, mut regex_match: alacritty_terminal::term::search::Match) {
self.next_match = loop {
if let Some(next_match) = self.hint_post_processing(&regex_match) {
self.start = next_match.end().add(self.term, Boundary::Grid, 1);
break Some(next_match);
}

self.start = regex_match.start().add(self.term, Boundary::Grid, 1);
if self.start > self.end {
return;
}

match self
.term
.regex_search_right(self.regex, self.start, self.end)
{
Some(rm) => regex_match = rm,
None => return,
}
};
}
}

impl<'a, T> Iterator for HintPostProcessor<'a, T> {
type Item = alacritty_terminal::term::search::Match;

fn next(&mut self) -> Option<Self::Item> {
let next_match = self.next_match.take()?;

if self.start <= self.end {
if let Some(rm) = self
.term
.regex_search_right(self.regex, self.start, self.end)
{
self.next_processed_match(rm);
}
}

Some(next_match)
}
}

impl Drop for Terminal {
fn drop(&mut self) {
// Ensure shutdown on terminal drop
Expand Down

0 comments on commit 75e1082

Please sign in to comment.