diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 72aa28cb80..bf5c98d03c 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -14,7 +14,7 @@ use alacritty_terminal::{ selection::{Selection, SelectionRange, SelectionType}, sync::FairMutex, term::{ - cell::{Cell, Hyperlink}, + cell::Cell, color::Rgb, search::{Match, RegexIter, RegexSearch}, RenderableCursor, TermMode, @@ -43,7 +43,7 @@ use std::{ collections::{HashMap, VecDeque}, fmt::Display, io, - ops::{Deref, Range, RangeInclusive, Sub}, + ops::{Deref, RangeInclusive, Sub}, os::unix::{prelude::AsRawFd, process::CommandExt}, path::PathBuf, process::Command, @@ -77,16 +77,14 @@ pub fn init(cx: &mut MutableAppContext) { ///Scroll multiplier that is set to 3 by default. This will be removed when I ///Implement scroll bars. const SCROLL_MULTIPLIER: f32 = 4.; -// const MAX_SEARCH_LINES: usize = 100; +const MAX_SEARCH_LINES: usize = 100; const DEBUG_TERMINAL_WIDTH: f32 = 500.; const DEBUG_TERMINAL_HEIGHT: f32 = 30.; const DEBUG_CELL_WIDTH: f32 = 5.; const DEBUG_LINE_HEIGHT: f32 = 5.; /// Copied from alacritty's ui_config.rs -const URL_REGEX: &str = - "(ipfs:|ipns:|magnet:|mailto:|gemini:|gopher:|https:|http:|news:|file:|git:|ssh:|ftp:)\ - [^\u{0000}-\u{001F}\u{007F}-\u{009F}<>\"\\s{-}\\^⟨⟩`]+"; +static URL_REGEX: RegexSearch = RegexSearch::new("(ipfs:|ipns:|magnet:|mailto:|gemini:|gopher:|https:|http:|news:|file:|git:|ssh:|ftp:)[^\u{0000}-\u{001F}\u{007F}-\u{009F}<>\"\\s{-}\\^⟨⟩`]+").unwrap(); ///Upward flowing events, for changing the title and such #[derive(Clone, Copy, Debug)] @@ -500,7 +498,7 @@ pub struct Terminal { pub matches: Vec>, last_content: TerminalContent, last_synced: Instant, - last_hovered_hyperlink: Option<(Hyperlink, Range)>, + last_hovered_hyperlink: Option<(String, RangeInclusive)>, sync_task: Option>, selection_head: Option, breadcrumb_text: String, @@ -651,14 +649,23 @@ impl Terminal { } InternalEvent::ScrollToPoint(point) => term.scroll_to_point(*point), InternalEvent::Hyperlink(position, open) => { + self.last_hovered_hyperlink = None; + let point = grid_point( *position, self.last_content.size, term.grid().display_offset(), ); - let side = mouse_side(*position, self.last_content.size); - println!("Hyperlink hover | click ") + if let Some(url_match) = regex_match_at(term, point, &URL_REGEX) { + let url = term.bounds_to_string(*url_match.start(), *url_match.end()); + + if *open { + open_uri(&url).log_err(); + } else { + self.last_hovered_hyperlink = Some((url, url_match)); + } + } } } } @@ -872,9 +879,9 @@ impl Terminal { self.last_hovered_hyperlink = link.map(|link| { ( - link, + link.uri().to_owned(), self.last_content.cells[min_index].point - ..self.last_content.cells[max_index].point, + ..=self.last_content.cells[max_index].point, ) }); } else { @@ -1112,6 +1119,30 @@ impl Entity for Terminal { type Event = Event; } +/// Based on alacritty/src/display/hint.rs > regex_match_at +/// Retrieve the match, if the specified point is inside the content matching the regex. +fn regex_match_at(term: &Term, point: Point, regex: &RegexSearch) -> Option { + visible_regex_match_iter(term, regex).find(|rm| rm.contains(&point)) +} + +/// Copied from alacritty/src/display/hint.rs: +/// Iterate over all visible regex matches. +pub 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, AlacDirection::Right, term, regex) + .skip_while(move |rm| rm.end().line < viewport_start) + .take_while(move |rm| rm.start().line <= viewport_end) +} + fn make_selection(range: &RangeInclusive) -> Selection { let mut selection = Selection::new(SelectionType::Simple, *range.start(), AlacDirection::Left); selection.update(*range.end(), AlacDirection::Right);