Repository: alacritty/alacritty (language: rust)

Issue:
Hyperlinks not working always
### System

OS: Linux (Archlinux)
Version: alacritty 0.14.0 (22a44757)
Linux/BSD: Gnome 47.1 + Wayland

There is something wrong with hyperlinks. I have opened two terminals: Alacritty and Gnome Console. On Gnome Console, hyperlinks are working just fine, on Alacritty I'm not sure how but when trying copy pasting the prompt or some gibberish on it, then it starts working



https://github.com/user-attachments/assets/20b78598-4364-4106-9570-445d3c927f75




Discussion / hints:
Sounds like a shell issue. The shell is likely rewriting itself when you're pasting text in thus fixing the missing hyperlink escape. 
But it's working fine on another terminal by default, we sure it's not an alacritty issue?
It would help to see what the format of the escape sequence you're using to create the link.

Chris is probably right, and the reason it could be different with another terminal is because not all terminals handle prompts (reflowing text) the same way.
> Chris is probably right, and the reason it could be different with another terminal is because not all terminals handle prompts (reflowing text) the same way.

Startup time and feature detection might also impact this.
I've checked since I was pretty sure this was working at some point, looks like maybe some regression on 0.14? I downgraded to 0.13 and recorded this video


https://github.com/user-attachments/assets/aef38c33-22e9-4611-bfc6-5529a17c725f

TLDR: On 0.13 works fine, I upgrade to 0.14 and then it stops working (The cursor is not clickable and the URI is not showing up on the bottom)
Does it work on 0.14 if you run `alacritty -V` first and repaint the prompt like in the second video?
If I'm guessing correctly what you mean, looks to be handling the hyperlinks fine on old prompts


https://github.com/user-attachments/assets/68884a3f-c894-46e3-8059-7cd7f3ac7b41


Is it then something on Alacritty or something I should report somewhere else?
You can always try bisecting if you feel like it's a regression, but it could just be timing issue.
This does look like a bug in alacritty. I can reproduce it in bash with:

```sh
PS1='\e]8;;https://nixpulvis.com\anixpulvis\e]8;;\a $ '
```

The current prompt always fails to be a clickable URL though the cursor does change and flicker a bit. This makes me think it's not a timing issue.
Tried bisecting, got this:

```
a1ed79bd2c014be49a85e2100ce374b86c8839e8 is the first bad commit
commit a1ed79bd2c014be49a85e2100ce374b86c8839e8
Author: Christian Duerr <contact@christianduerr.com>
Date:   Sat Oct 5 11:39:06 2024 +0200

    Fix highlight invalidation on grid scroll
    
    This fixes an issue where hints highlighted by vi or mouse cursor would
    produce an underline on the incorrect line since the highlights only
    store the initial match boundaries without accounting for new content
    scrolling the terminal.
    
    To accurately invalidate the hint highlights, we use existing damage
    information of the current frame. The existing logic to damage hints for
    the next frame to account for removal has been changed, since the hints
    would otherwise be cleared immediately. Instead we now mark the terminal
    as fully damaged for the upcoming frame whenever the hints are cleared.
    
    Closes #7737.

 CHANGELOG.md                        |   1 +
 alacritty/src/display/damage.rs     |   9 +++++
 alacritty/src/display/mod.rs        | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------
 alacritty_terminal/src/term/cell.rs |   2 --
 4 files changed, 107 insertions(+), 82 deletions(-)
```
Recloned and retried going to the same commit and parent and reproduced, (Trying to figure out if it's something about timing)


https://github.com/user-attachments/assets/d0b3e49c-07de-4c05-8041-dea0abbd1ab4


I also see a1ed79bd2c014be49a85e2100ce374b86c8839e8 introducing this issue when I bisect. I don't think it's a timing or reflow issue since the bug appears in a short prompt (see my above comment) and it's always present for the current prompt. Other prompts work as expected.
Very possible for this patch to have introduced a regression.
If it helps, ping me whenever you want me to test any patch ;)
Here's a video showcasing the issue a bit more broadly:
![damage](https://github.com/user-attachments/assets/c372715a-e24b-4bed-982c-002ec247d4bb)

Can confirm that a1ed79bd2c014be49a85e2100ce374b86c8839e8 introduced the regression. Here is a simple reproducer (just a hyperlink with one-character label, everything bigger than one char still works fine):
```sh
printf '\e]8;id=1235;https://example.com\e\\A\e]8;;\e\\\n'
```

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 75f164d0043..095d5fd7bd5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,10 @@ Notable changes to the `alacritty_terminal` crate are documented in its
 
 ## 0.15.0-dev
 
+### Fixed
+
+- Mouse/Vi cursor hint highlighting broken on the terminal cursor line
+
 ## 0.14.0
 
 ### Packaging
diff --git a/alacritty/src/display/damage.rs b/alacritty/src/display/damage.rs
index 8efe0133ba8..fc6aef39f32 100644
--- a/alacritty/src/display/damage.rs
+++ b/alacritty/src/display/damage.rs
@@ -193,9 +193,11 @@ impl FrameDamage {
     /// Check if a range is damaged.
     #[inline]
     pub fn intersects(&self, start: Point<usize>, end: Point<usize>) -> bool {
+        let start_line = &self.lines[start.line];
+        let end_line = &self.lines[end.line];
         self.full
-            || self.lines[start.line].left <= start.column
-            || self.lines[end.line].right >= end.column
+            || (start_line.left..=start_line.right).contains(&start.column)
+            || (end_line.left..=end_line.right).contains(&end.column)
             || (start.line + 1..end.line).any(|line| self.lines[line].is_damaged())
     }
 }
diff --git a/alacritty/src/display/mod.rs b/alacritty/src/display/mod.rs
index d0044f8bf15..d358a7c5d26 100644
--- a/alacritty/src/display/mod.rs
+++ b/alacritty/src/display/mod.rs
@@ -342,9 +342,13 @@ pub struct Display {
 
     /// Hint highlighted by the mouse.
     pub highlighted_hint: Option<HintMatch>,
+    /// Frames since hint highlight was created.
+    highlighted_hint_age: usize,
 
     /// Hint highlighted by the vi mode cursor.
     pub vi_highlighted_hint: Option<HintMatch>,
+    /// Frames since hint highlight was created.
+    vi_highlighted_hint_age: usize,
 
     pub raw_window_handle: RawWindowHandle,
 
@@ -516,6 +520,8 @@ impl Display {
             font_size,
             window,
             pending_renderer_update: Default::default(),
+            vi_highlighted_hint_age: Default::default(),
+            highlighted_hint_age: Default::default(),
             vi_highlighted_hint: Default::default(),
             highlighted_hint: Default::default(),
             hint_mouse_point: Default::default(),
@@ -759,7 +765,7 @@ impl Display {
         drop(terminal);
 
         // Invalidate highlighted hints if grid has changed.
-        self.validate_hints(display_offset);
+        self.validate_hint_highlights(display_offset);
 
         // Add damage from alacritty's UI elements overlapping terminal.
 
@@ -1017,9 +1023,10 @@ impl Display {
         };
         let mut dirty = vi_highlighted_hint != self.vi_highlighted_hint;
         self.vi_highlighted_hint = vi_highlighted_hint;
+        self.vi_highlighted_hint_age = 0;
 
         // Force full redraw if the vi mode highlight was cleared.
-        if dirty && self.vi_highlighted_hint.is_none() {
+        if dirty {
             self.damage_tracker.frame().mark_fully_damaged();
         }
 
@@ -1055,9 +1062,10 @@ impl Display {
         let mouse_highlight_dirty = self.highlighted_hint != highlighted_hint;
         dirty |= mouse_highlight_dirty;
         self.highlighted_hint = highlighted_hint;
+        self.highlighted_hint_age = 0;
 
-        // Force full redraw if the mouse cursor highlight was cleared.
-        if mouse_highlight_dirty && self.highlighted_hint.is_none() {
+        // Force full redraw if the mouse cursor highlight was changed.
+        if mouse_highlight_dirty {
             self.damage_tracker.frame().mark_fully_damaged();
         }
 
@@ -1331,16 +1339,24 @@ impl Display {
     }
 
     /// Check whether a hint highlight needs to be cleared.
-    fn validate_hints(&mut self, display_offset: usize) {
+    fn validate_hint_highlights(&mut self, display_offset: usize) {
         let frame = self.damage_tracker.frame();
-        for (hint, reset_mouse) in
-            [(&mut self.highlighted_hint, true), (&mut self.vi_highlighted_hint, false)]
-        {
+        let hints = [
+            (&mut self.highlighted_hint, &mut self.highlighted_hint_age, true),
+            (&mut self.vi_highlighted_hint, &mut self.vi_highlighted_hint_age, false),
+        ];
+        for (hint, hint_age, reset_mouse) in hints {
             let (start, end) = match hint {
                 Some(hint) => (*hint.bounds().start(), *hint.bounds().end()),
-                None => return,
+                None => continue,
             };
 
+            // Ignore hints that were created this frame.
+            *hint_age += 1;
+            if *hint_age == 1 {
+                continue;
+            }
+
             // Convert hint bounds to viewport coordinates.
             let start = term::point_to_viewport(display_offset, start).unwrap_or_default();
             let end = term::point_to_viewport(display_offset, end).unwrap_or_else(|| {


Test patch: