Repository: alacritty/alacritty (language: rust)

Issue:
Bug: backward search on single line goes in cycles
When using backward search, it sometimes goes in cycles without moving a cursor to some search result. In example video, before resizing, everything works fine, but after resizing, it cycles only on several last elements on the line, instead of cycling through all occurrences

https://github.com/alacritty/alacritty/assets/37012324/34cbb259-9d3d-4de5-83df-f92ca66735ba

### System

OS: Linux, Wayland, Sway
Version: alacritty 0.13.2

### Logs

No logs



Discussion / hints:
The provided video cannot be played back.
@chrisduerr , I can play it back. Do you use firefox? If so, videos in github with firefox is a known bug. Try downloading video or using another browser :shrug: 
Even without video, try to create a long line with like 30 or 40 entries of the same word and try backward-searching this word. Search will break

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 97d3de8419b..8442355ae5c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,6 +31,7 @@ Notable changes to the `alacritty_terminal` crate are documented in its
 - Leaking FDs when closing windows on Unix systems
 - Config emitting errors for nonexistent import paths
 - Kitty keyboard protocol reporting shifted key codes
+- Broken search with words broken across line boundary on the first character
 
 ## 0.13.2
 
diff --git a/alacritty/src/renderer/text/glyph_cache.rs b/alacritty/src/renderer/text/glyph_cache.rs
index 957cde1a3dc..6acc3189a0b 100644
--- a/alacritty/src/renderer/text/glyph_cache.rs
+++ b/alacritty/src/renderer/text/glyph_cache.rs
@@ -187,14 +187,9 @@ impl GlyphCache {
     ///
     /// This will fail when the glyph could not be rasterized. Usually this is due to the glyph
     /// not being present in any font.
-    pub fn get<L: ?Sized>(
-        &mut self,
-        glyph_key: GlyphKey,
-        loader: &mut L,
-        show_missing: bool,
-    ) -> Glyph
+    pub fn get<L>(&mut self, glyph_key: GlyphKey, loader: &mut L, show_missing: bool) -> Glyph
     where
-        L: LoadGlyph,
+        L: LoadGlyph + ?Sized,
     {
         // Try to load glyph from cache.
         if let Some(glyph) = self.cache.get(&glyph_key) {
@@ -242,9 +237,9 @@ impl GlyphCache {
     /// Load glyph into the atlas.
     ///
     /// This will apply all transforms defined for the glyph cache to the rasterized glyph before
-    pub fn load_glyph<L: ?Sized>(&self, loader: &mut L, mut glyph: RasterizedGlyph) -> Glyph
+    pub fn load_glyph<L>(&self, loader: &mut L, mut glyph: RasterizedGlyph) -> Glyph
     where
-        L: LoadGlyph,
+        L: LoadGlyph + ?Sized,
     {
         glyph.left += i32::from(self.glyph_offset.x);
         glyph.top += i32::from(self.glyph_offset.y);
diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs
index 4113ed9c381..5dfd880cddb 100644
--- a/alacritty_terminal/src/term/mod.rs
+++ b/alacritty_terminal/src/term/mod.rs
@@ -1445,15 +1445,15 @@ impl<T: EventListener> Handler for Term<T> {
     /// edition, in LINE FEED mode,
     ///
     /// > The execution of the formatter functions LINE FEED (LF), FORM FEED
-    /// (FF), LINE TABULATION (VT) cause only movement of the active position in
-    /// the direction of the line progression.
+    /// > (FF), LINE TABULATION (VT) cause only movement of the active position in
+    /// > the direction of the line progression.
     ///
     /// In NEW LINE mode,
     ///
     /// > The execution of the formatter functions LINE FEED (LF), FORM FEED
-    /// (FF), LINE TABULATION (VT) cause movement to the line home position on
-    /// the following line, the following form, etc. In the case of LF this is
-    /// referred to as the New Line (NL) option.
+    /// > (FF), LINE TABULATION (VT) cause movement to the line home position on
+    /// > the following line, the following form, etc. In the case of LF this is
+    /// > referred to as the New Line (NL) option.
     ///
     /// Additionally, ECMA-48 4th edition says that this option is deprecated.
     /// ECMA-48 5th edition only mentions this option (without explanation)
@@ -2187,7 +2187,7 @@ impl<T: EventListener> Handler for Term<T> {
     fn set_title(&mut self, title: Option<String>) {
         trace!("Setting title to '{:?}'", title);
 
-        self.title = title.clone();
+        self.title.clone_from(&title);
 
         let title_event = match title {
             Some(title) => Event::Title(title),
diff --git a/alacritty_terminal/src/term/search.rs b/alacritty_terminal/src/term/search.rs
index 585e191c0fa..a5ae9337a96 100644
--- a/alacritty_terminal/src/term/search.rs
+++ b/alacritty_terminal/src/term/search.rs
@@ -293,12 +293,12 @@ impl<T> Term<T> {
         let mut state = regex.dfa.start_state_forward(&mut regex.cache, &input).unwrap();
 
         let mut iter = self.grid.iter_from(start);
-        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, regex.direction);
+        let mut last_wrapped = cell.flags.contains(Flags::WRAPLINE);
         let mut c = cell.c;
 
         let mut point = iter.point();
@@ -1155,4 +1155,20 @@ mod tests {
         assert_eq!(start, Point::new(Line(1), Column(0)));
         assert_eq!(end, Point::new(Line(1), Column(2)));
     }
+
+    #[test]
+    fn inline_word_search() {
+        #[rustfmt::skip]
+        let term = mock_term("\
+            word word word word w\n\
+            ord word word word\
+        ");
+
+        let mut regex = RegexSearch::new("word").unwrap();
+        let start = Point::new(Line(1), Column(4));
+        let end = Point::new(Line(0), Column(0));
+        let match_start = Point::new(Line(0), Column(20));
+        let match_end = Point::new(Line(1), Column(2));
+        assert_eq!(term.regex_search_left(&mut regex, start, end), Some(match_start..=match_end));
+    }
 }


Test patch: