Do not flicker when switching cmd-hovered words in terminal (#30098)

Closes https://github.com/zed-industries/zed/issues/25110


https://github.com/user-attachments/assets/4624c256-8dfb-48eb-a726-6cf130d946da

Terminal may update its hovered word way before reporting it to the
terminal view, and that processing the file check later.
Hence, store the terminal hover data in the terminal view and avoid
highlights when it's different from what the terminal has (as the source
of truth here).

In addition, now only does hover refreshes when the terminal hover
actually changes, not on every event report.

Release Notes:

- Fixed underline flicker when switching cmd-hovered words in terminal
This commit is contained in:
Kirill Bulatov 2025-05-07 14:04:11 +03:00 committed by GitHub
parent c19a5c2fd6
commit f7e77123cc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 102 additions and 62 deletions

View file

@ -730,29 +730,38 @@ impl Element for TerminalElement {
let background_color = theme.colors().terminal_background;
let (last_hovered_word, hover_target) = self.terminal.update(cx, |terminal, cx| {
terminal.set_size(dimensions);
terminal.sync(window, cx);
let (last_hovered_word, hover_tooltip) =
self.terminal.update(cx, |terminal, cx| {
terminal.set_size(dimensions);
terminal.sync(window, cx);
if window.modifiers().secondary()
&& bounds.contains(&window.mouse_position())
&& self.terminal_view.read(cx).hover_target_tooltip.is_some()
{
let hover_target = self.terminal_view.read(cx).hover_target_tooltip.clone();
let last_hovered_word = terminal.last_content.last_hovered_word.clone();
(last_hovered_word, hover_target)
} else {
(None, None)
}
});
if window.modifiers().secondary()
&& bounds.contains(&window.mouse_position())
&& self.terminal_view.read(cx).hover.is_some()
{
let registered_hover = self.terminal_view.read(cx).hover.as_ref();
if terminal.last_content.last_hovered_word.as_ref()
== registered_hover.map(|hover| &hover.hovered_word)
{
(
terminal.last_content.last_hovered_word.clone(),
registered_hover.map(|hover| hover.tooltip.clone()),
)
} else {
(None, None)
}
} else {
(None, None)
}
});
let scroll_top = self.terminal_view.read(cx).scroll_top;
let hyperlink_tooltip = hover_target.as_ref().map(|hover_target| {
let hyperlink_tooltip = hover_tooltip.map(|hover_tooltip| {
let offset = bounds.origin + point(gutter, px(0.)) - point(px(0.), scroll_top);
let mut element = div()
.size_full()
.id("terminal-element")
.tooltip(Tooltip::text(hover_target.clone()))
.tooltip(Tooltip::text(hover_tooltip))
.into_any_element();
element.prepaint_as_root(offset, bounds.size.into(), window, cx);
element
@ -922,7 +931,7 @@ impl Element for TerminalElement {
self.register_mouse_listeners(layout.mode, &layout.hitbox, window);
if window.modifiers().secondary()
&& bounds.contains(&window.mouse_position())
&& self.terminal_view.read(cx).hover_target_tooltip.is_some()
&& self.terminal_view.read(cx).hover.is_some()
{
window.set_cursor_style(gpui::CursorStyle::PointingHand, Some(&layout.hitbox));
} else {