Fix fullwidth char regex search infinite loop
This resolves an issue where the regex search could loop indefinitely
when the end point was defined in a location containing a fullwidth
character, thus skipping over the end before termination.
Fixes #5753.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d3df994..16743e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -39,6 +39,7 @@
- Freeze when the vi cursor is on the scrollback and scrollback clear is invoked
- Vi cursor on topmost of the display moving downward when scrolled into history with active output
- Input lag on Wayland with Nvidia binary driver
+- Crash when hovering the mouse over fullwidth characters
### Removed
diff --git a/alacritty_terminal/src/term/search.rs b/alacritty_terminal/src/term/search.rs
index 07403b6..e34fd1b 100644
--- a/alacritty_terminal/src/term/search.rs
+++ b/alacritty_terminal/src/term/search.rs
@@ -205,6 +205,7 @@
let mut state = dfa.start_state();
let mut last_wrapped = false;
let mut regex_match = None;
+ let mut done = false;
let mut cell = iter.cell();
self.skip_fullwidth(&mut iter, &mut cell, direction);
@@ -239,7 +240,7 @@
}
// Stop once we've reached the target point.
- if point == end {
+ if point == end || done {
break;
}
@@ -254,7 +255,12 @@
iter.cell()
},
};
+
+ // Check for completion before potentially skipping over fullwidth characters.
+ done = iter.point() == end;
+
self.skip_fullwidth(&mut iter, &mut cell, direction);
+
let wrapped = cell.flags.contains(Flags::WRAPLINE);
c = cell.c;
@@ -701,6 +707,26 @@
}
#[test]
+ fn end_on_fullwidth() {
+ let term = mock_term("jarr🦇");
+
+ let start = Point::new(Line(0), Column(0));
+ let end = Point::new(Line(0), Column(4));
+
+ // Ensure ending without a match doesn't loop indefinitely.
+ let dfas = RegexSearch::new("x").unwrap();
+ assert_eq!(term.regex_search_right(&dfas, start, end), None);
+
+ let dfas = RegexSearch::new("x").unwrap();
+ let match_end = Point::new(Line(0), Column(5));
+ assert_eq!(term.regex_search_right(&dfas, start, match_end), None);
+
+ // Ensure match is captured when only partially inside range.
+ let dfas = RegexSearch::new("jarr🦇").unwrap();
+ assert_eq!(term.regex_search_right(&dfas, start, end), Some(start..=match_end));
+ }
+
+ #[test]
fn wrapping() {
#[rustfmt::skip]
let term = mock_term("\