Finished terminal hyperlinks for now

This commit is contained in:
Mikayla Maki 2022-09-26 16:33:29 -07:00
parent d2d49633f1
commit 4bc0afdafa
3 changed files with 124 additions and 73 deletions

View file

@ -385,6 +385,8 @@ impl TerminalBuilder {
breadcrumb_text: String::new(),
scroll_px: 0.,
last_mouse_position: None,
next_link_id: 0,
selection_phase: SelectionPhase::Ended,
};
Ok(TerminalBuilder {
@ -471,7 +473,7 @@ pub struct TerminalContent {
cursor: RenderableCursor,
cursor_char: char,
size: TerminalSize,
last_hovered_hyperlink: Option<(String, RangeInclusive<Point>)>,
last_hovered_hyperlink: Option<(String, RangeInclusive<Point>, usize)>,
}
impl Default for TerminalContent {
@ -493,6 +495,12 @@ impl Default for TerminalContent {
}
}
#[derive(PartialEq, Eq)]
pub enum SelectionPhase {
Selecting,
Ended,
}
pub struct Terminal {
pty_tx: Notifier,
term: Arc<FairMutex<Term<ZedListener>>>,
@ -511,6 +519,8 @@ pub struct Terminal {
shell_fd: u32,
foreground_process_info: Option<LocalProcessInfo>,
scroll_px: f32,
next_link_id: usize,
selection_phase: SelectionPhase,
}
impl Terminal {
@ -654,7 +664,7 @@ impl Terminal {
}
InternalEvent::ScrollToPoint(point) => term.scroll_to_point(*point),
InternalEvent::Hyperlink(position, open) => {
self.last_content.last_hovered_hyperlink = None;
let prev_hyperlink = self.last_content.last_hovered_hyperlink.take();
let point = grid_point(
*position,
@ -668,13 +678,37 @@ impl Terminal {
if *open {
open_uri(&url).log_err();
} else {
self.last_content.last_hovered_hyperlink = Some((url, url_match));
self.update_hyperlink(prev_hyperlink, url, url_match);
}
}
}
}
}
fn update_hyperlink(
&mut self,
prev_hyperlink: Option<(String, RangeInclusive<Point>, usize)>,
url: String,
url_match: RangeInclusive<Point>,
) {
if let Some(prev_hyperlink) = prev_hyperlink {
if prev_hyperlink.0 == url && prev_hyperlink.1 == url_match {
self.last_content.last_hovered_hyperlink = Some((url, url_match, prev_hyperlink.2));
} else {
self.last_content.last_hovered_hyperlink =
Some((url, url_match, self.next_link_id()));
}
} else {
self.last_content.last_hovered_hyperlink = Some((url, url_match, self.next_link_id()));
}
}
fn next_link_id(&mut self) -> usize {
let res = self.next_link_id;
self.next_link_id = self.next_link_id.wrapping_add(1);
res
}
pub fn last_content(&self) -> &TerminalContent {
&self.last_content
}
@ -846,7 +880,8 @@ impl Terminal {
}
pub fn mouse_move(&mut self, e: &MouseMovedEvent, origin: Vector2F) {
self.last_content.last_hovered_hyperlink = None;
let prev_hyperlink = self.last_content.last_hovered_hyperlink.take();
let position = e.position.sub(origin);
self.last_mouse_position = Some(position);
if self.mouse_mode(e.shift) {
@ -862,19 +897,26 @@ impl Terminal {
self.pty_tx.notify(bytes);
}
}
} else if e.cmd {
self.fill_hyperlink(Some(position));
} else {
self.fill_hyperlink(Some(position), prev_hyperlink);
}
}
fn fill_hyperlink(&mut self, position: Option<Vector2F>) {
if let Some(position) = position {
fn fill_hyperlink(
&mut self,
position: Option<Vector2F>,
prev_hyperlink: Option<(String, RangeInclusive<Point>, usize)>,
) {
if self.selection_phase == SelectionPhase::Selecting {
self.last_content.last_hovered_hyperlink = None;
} else if let Some(position) = position {
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 {
if min_index >= 1 && self.last_content.cells[min_index - 1].hyperlink() == link
{
min_index = min_index - 1;
} else {
break;
@ -882,21 +924,24 @@ impl Terminal {
}
let mut max_index = content_index;
let len = self.last_content.cells.len();
loop {
if self.last_content.cells[max_index + 1].hyperlink() == link {
if max_index < len - 1
&& self.last_content.cells[max_index + 1].hyperlink() == link
{
max_index = max_index + 1;
} else {
break;
}
}
self.last_content.last_hovered_hyperlink = link.map(|link| {
(
link.uri().to_owned(),
self.last_content.cells[min_index].point
..=self.last_content.cells[max_index].point,
)
});
if let Some(link) = link {
let url = link.uri().to_owned();
let url_match = self.last_content.cells[min_index].point
..=self.last_content.cells[max_index].point;
self.update_hyperlink(prev_hyperlink, url, url_match);
};
} else {
self.events
.push_back(InternalEvent::Hyperlink(position, false));
@ -909,6 +954,7 @@ impl Terminal {
self.last_mouse_position = Some(position);
if !self.mouse_mode(e.shift) {
self.selection_phase = SelectionPhase::Selecting;
// Alacritty has the same ordering, of first updating the selection
// then scrolling 15ms later
self.events
@ -969,7 +1015,9 @@ impl Terminal {
pub fn left_click(&mut self, e: &ClickRegionEvent, origin: Vector2F) {
let position = e.position.sub(origin);
if !self.mouse_mode(e.shift) {
if e.cmd {
if self.last_content.last_hovered_hyperlink.is_some()
&& self.last_content.selection.is_none()
{
let mouse_cell_index = content_index_for_mouse(position, &self.last_content);
if let Some(link) = self.last_content.cells[mouse_cell_index].hyperlink() {
open_uri(link.uri()).log_err();
@ -1021,6 +1069,7 @@ impl Terminal {
// so let's do that here
self.copy();
}
self.selection_phase = SelectionPhase::Ended;
self.last_mouse = None;
}
@ -1061,10 +1110,10 @@ impl Terminal {
}
pub fn refresh_hyperlink(&mut self, cmd: bool) -> bool {
self.last_content.last_hovered_hyperlink = None;
let prev_hyperlink = self.last_content.last_hovered_hyperlink.take();
if cmd {
self.fill_hyperlink(self.last_mouse_position);
self.fill_hyperlink(self.last_mouse_position, prev_hyperlink);
true
} else {
false

View file

@ -7,7 +7,7 @@ use alacritty_terminal::{
use editor::{Cursor, CursorShape, HighlightedRange, HighlightedRangeLine};
use gpui::{
color::Color,
elements::{Overlay, Tooltip},
elements::{Empty, Overlay},
fonts::{HighlightStyle, Properties, Style::Italic, TextStyle, Underline, Weight},
geometry::{
rect::RectF,
@ -15,7 +15,7 @@ use gpui::{
},
serde_json::json,
text_layout::{Line, RunStyle},
Axis, Element, ElementBox, Event, EventContext, FontCache, KeyDownEvent, ModelContext,
Element, ElementBox, Event, EventContext, FontCache, KeyDownEvent, ModelContext,
ModifiersChangedEvent, MouseButton, MouseRegion, PaintContext, Quad, SizeConstraint,
TextLayoutCache, WeakModelHandle, WeakViewHandle,
};
@ -324,7 +324,6 @@ impl TerminalElement {
.unwrap_or_default();
if indexed.cell.hyperlink().is_some() {
underline.squiggly = true;
if underline.thickness == OrderedFloat(0.) {
underline.thickness = OrderedFloat(1.);
}
@ -594,51 +593,34 @@ impl Element for TerminalElement {
};
let terminal_handle = self.terminal.upgrade(cx).unwrap();
let (last_hovered_hyperlink, last_mouse) =
terminal_handle.update(cx.app, |terminal, cx| {
terminal.set_size(dimensions);
terminal.try_sync(cx);
(
terminal.last_content.last_hovered_hyperlink.clone(),
terminal.last_mouse_position,
)
});
let last_hovered_hyperlink = terminal_handle.update(cx.app, |terminal, cx| {
terminal.set_size(dimensions);
terminal.try_sync(cx);
terminal.last_content.last_hovered_hyperlink.clone()
});
let view_handle = self.view.clone();
let hyperlink_tooltip = last_hovered_hyperlink.and_then(|(uri, _)| {
last_mouse.and_then(|last_mouse| {
view_handle.upgrade(cx).map(|handle| {
let mut tooltip = cx.render(&handle, |_, cx| {
// TODO: Use the correct dynamic line height
// let mut collapsed_tooltip = Tooltip::render_tooltip(
// uri.clone(),
// tooltip_style.clone(),
// None,
// false,
// )
// .boxed();
let hyperlink_tooltip = last_hovered_hyperlink.and_then(|(uri, _, id)| {
// last_mouse.and_then(|_last_mouse| {
view_handle.upgrade(cx).map(|handle| {
let mut tooltip = cx.render(&handle, |_, cx| {
Overlay::new(
Empty::new()
.contained()
.constrained()
.with_width(dimensions.width())
.with_height(dimensions.height())
.with_tooltip::<TerminalElement, _>(id, uri, None, tooltip_style, cx)
.boxed(),
)
.with_position_mode(gpui::elements::OverlayPositionMode::Local)
.boxed()
});
Overlay::new(
Tooltip::render_tooltip(uri, tooltip_style, None, false)
.constrained()
.with_height(text_style.line_height(cx.font_cache()))
// .dynamically(move |constraint, cx| {
// SizeConstraint::strict_along(
// Axis::Vertical,
// collapsed_tooltip.layout(constraint, cx).y(),
// )
// })
.boxed(),
)
.with_fit_mode(gpui::elements::OverlayFitMode::SwitchAnchor)
.with_anchor_position(last_mouse)
.boxed()
});
tooltip.layout(SizeConstraint::new(Vector2F::zero(), cx.window_size), cx);
tooltip
})
tooltip.layout(SizeConstraint::new(Vector2F::zero(), cx.window_size), cx);
tooltip
})
// })
});
let TerminalContent {
@ -672,7 +654,7 @@ impl Element for TerminalElement {
self.modal,
last_hovered_hyperlink
.as_ref()
.map(|(_, range)| (link_style, range)),
.map(|(_, range, _)| (link_style, range)),
);
//Layout cursor. Rectangle is used for IME, so we should lay it out even
@ -822,12 +804,7 @@ impl Element for TerminalElement {
}
if let Some(element) = &mut layout.hyperlink_tooltip {
element.paint(
visible_bounds.lower_left()
- vec2f(-layout.size.cell_width, layout.size.line_height),
visible_bounds,
cx,
)
element.paint(origin, visible_bounds, cx)
}
});
}