From b8f362fd843c50c2a76eb21595a8566e85d234f6 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 19 Sep 2022 13:41:35 -0700 Subject: [PATCH] WIP hyperlink detection --- crates/terminal/src/terminal.rs | 81 +++++++++++++++++++++---- crates/terminal/src/terminal_element.rs | 25 +++----- 2 files changed, 80 insertions(+), 26 deletions(-) diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 9da2831740..e67f3bdbb0 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -83,6 +83,11 @@ 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{-}\\^⟨⟩`]+"; + ///Upward flowing events, for changing the title and such #[derive(Clone, Copy, Debug)] pub enum Event { @@ -105,6 +110,7 @@ enum InternalEvent { ScrollToPoint(Point), SetSelection(Option<(Selection, Point)>), UpdateSelection(Vector2F), + HyperlinkHover(Vector2F), Copy, } @@ -643,6 +649,16 @@ impl Terminal { } } InternalEvent::ScrollToPoint(point) => term.scroll_to_point(*point), + InternalEvent::HyperlinkHover(position) => { + 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") + } } } @@ -817,7 +833,6 @@ impl Terminal { pub fn mouse_move(&mut self, e: &MouseMovedEvent, origin: Vector2F) { self.last_hovered_hyperlink = None; let position = e.position.sub(origin); - if self.mouse_mode(e.shift) { let point = grid_point( position, @@ -832,9 +847,39 @@ impl Terminal { } } } else if e.cmd { - let hyperlink = cell_for_mouse(e.position, &self.last_content) - .cell - .hyperlink(); + let content_index = content_index_for_mouse(position, &self.last_content); + let link = self.last_content.cells[content_index].hyperlink(); + + if link.is_some() { + let mut min_index = content_index; + loop { + if self.last_content.cells[min_index - 1].hyperlink() == link { + min_index = min_index - 1; + } else { + break; + } + } + + let mut max_index = content_index; + loop { + if self.last_content.cells[max_index + 1].hyperlink() == link { + max_index = max_index + 1; + } else { + break; + } + } + + self.last_hovered_hyperlink = link.map(|link| { + ( + link, + self.last_content.cells[min_index].point + ..self.last_content.cells[max_index].point, + ) + }); + } else { + self.events + .push_back(InternalEvent::HyperlinkHover(position)); + } } } @@ -903,7 +948,12 @@ impl Terminal { let position = e.position.sub(origin); if !self.mouse_mode(e.shift) { if e.cmd { - if let Some(link) = cell_for_mouse(position, &self.last_content).hyperlink() { + if let Some(link) = self.last_content.cells + [content_index_for_mouse(position, &self.last_content)] + .hyperlink() + { + dbg!(&link); + dbg!(&self.last_hovered_hyperlink); open_uri(link.uri()).log_err(); } } else { @@ -1073,7 +1123,7 @@ fn all_search_matches<'a, T>( RegexIter::new(start, end, AlacDirection::Right, term, regex) } -fn cell_for_mouse<'a>(pos: Vector2F, content: &'a TerminalContent) -> &'a IndexedCell { +fn content_index_for_mouse<'a>(pos: Vector2F, content: &'a TerminalContent) -> usize { let col = min( (pos.x() / content.size.cell_width()) as usize, content.size.columns() - 1, @@ -1083,7 +1133,7 @@ fn cell_for_mouse<'a>(pos: Vector2F, content: &'a TerminalContent) -> &'a Indexe content.size.screen_lines() - 1, ) as usize; - &content.cells[(line * content.size.columns() + col)] + line * content.size.columns() + col } fn open_uri(uri: &str) -> Result<(), std::io::Error> { @@ -1116,7 +1166,7 @@ mod tests { use gpui::geometry::vector::vec2f; use rand::{thread_rng, Rng}; - use crate::cell_for_mouse; + use crate::content_index_for_mouse; use self::terminal_test_context::TerminalTestContext; @@ -1153,7 +1203,10 @@ mod tests { rng.gen_range(min_col..max_col), ); - assert_eq!(cell_for_mouse(mouse_pos, &content).c, cells[j][i]); + assert_eq!( + content.cells[content_index_for_mouse(mouse_pos, &content)].c, + cells[j][i] + ); } } } @@ -1172,7 +1225,13 @@ mod tests { let (content, cells) = TerminalTestContext::create_terminal_content(size, &mut rng); - assert_eq!(cell_for_mouse(vec2f(-10., -10.), &content).c, cells[0][0]); - assert_eq!(cell_for_mouse(vec2f(1000., 1000.), &content).c, cells[9][9]); + assert_eq!( + content.cells[content_index_for_mouse(vec2f(-10., -10.), &content)].c, + cells[0][0] + ); + assert_eq!( + content.cells[content_index_for_mouse(vec2f(1000., 1000.), &content)].c, + cells[9][9] + ); } } diff --git a/crates/terminal/src/terminal_element.rs b/crates/terminal/src/terminal_element.rs index 51ebf63e5e..52949ef213 100644 --- a/crates/terminal/src/terminal_element.rs +++ b/crates/terminal/src/terminal_element.rs @@ -427,6 +427,16 @@ impl TerminalElement { position: e.position, }); } + }) + .on_move(move |event, cx| { + if cx.is_parent_view_focused() { + if let Some(conn_handle) = connection.upgrade(cx.app) { + conn_handle.update(cx.app, |terminal, cx| { + terminal.mouse_move(&event, origin); + cx.notify(); + }) + } + } }); // Mouse mode handlers: @@ -474,21 +484,6 @@ impl TerminalElement { ), ) } - //Mouse move manages both dragging and motion events - if mode.intersects(TermMode::MOUSE_DRAG | TermMode::MOUSE_MOTION) { - region = region - //TODO: This does not fire on right-mouse-down-move events. - .on_move(move |event, cx| { - if cx.is_parent_view_focused() { - if let Some(conn_handle) = connection.upgrade(cx.app) { - conn_handle.update(cx.app, |terminal, cx| { - terminal.mouse_move(&event, origin); - cx.notify(); - }) - } - } - }) - } cx.scene.push_mouse_region(region); }