Baseline: Improve selection rendering for large quantities from 270ms to 90ms

Piotr Osiewicz created

Change summary

crates/editor/src/editor.rs  | 64 +++++++++++++++++++++++++++++++++++
crates/editor/src/element.rs | 67 +++++++++++++------------------------
2 files changed, 86 insertions(+), 45 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -90,7 +90,7 @@ use std::{
     cmp::{self, Ordering, Reverse},
     mem,
     num::NonZeroU32,
-    ops::{ControlFlow, Deref, DerefMut, Range},
+    ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
     path::Path,
     sync::Arc,
     time::{Duration, Instant},
@@ -7549,6 +7549,68 @@ impl Editor {
         results
     }
 
+    pub fn selected_rows<T: 'static>(
+        &self,
+        search_range: Range<Anchor>,
+        display_snapshot: &DisplaySnapshot,
+        theme: &Theme,
+    ) -> Vec<RangeInclusive<u32>> {
+        let mut results = Vec::new();
+        let buffer = &display_snapshot.buffer_snapshot;
+        let Some((color_fetcher, ranges)) = self.background_highlights
+            .get(&TypeId::of::<T>()) else {
+                return vec![];
+            };
+
+        let color = color_fetcher(theme);
+        let start_ix = match ranges.binary_search_by(|probe| {
+            let cmp = probe.end.cmp(&search_range.start, buffer);
+            if cmp.is_gt() {
+                Ordering::Greater
+            } else {
+                Ordering::Less
+            }
+        }) {
+            Ok(i) | Err(i) => i,
+        };
+        let mut push_region = |start, end| {
+            if let (Some(start_display), Some(end_display)) = (start, end) {
+                results.push(start_display..=end_display);
+            }
+        };
+        let mut start_row = None;
+        let mut end_row = None;
+        for range in &ranges[start_ix..] {
+            if range.start.cmp(&search_range.end, buffer).is_ge() {
+                break;
+            }
+            let start = range.start.to_point(buffer).row;
+            let end = range.end.to_point(buffer).row;
+            if start_row.is_none() {
+                assert_eq!(end_row, None);
+                start_row = Some(start);
+                end_row = Some(end);
+                continue;
+            }
+            if let Some(current_end) = end_row.as_mut() {
+                if start > *current_end + 1 {
+                    push_region(start_row, end_row);
+                    start_row = Some(start);
+                    end_row = Some(end);
+                } else {
+                    // Merge two hunks.
+                    *current_end = end;
+                }
+            } else {
+                unreachable!();
+            }
+        }
+        // We might still have a hunk that was not rendered (if there was a search hit on the last line)
+        push_region(start_row, end_row);
+
+        results
+    }
+
     pub fn highlight_text<T: 'static>(
         &mut self,
         ranges: Vec<Range<Anchor>>,

crates/editor/src/element.rs 🔗

@@ -1107,8 +1107,6 @@ impl EditorElement {
             if layout.is_singleton && scrollbar_settings.selections {
                 let start_anchor = Anchor::min();
                 let end_anchor = Anchor::max();
-                let mut start_row = None;
-                let mut end_row = None;
                 let color = scrollbar_theme.selections;
                 let border = Border {
                     width: 1.,
@@ -1119,54 +1117,35 @@ impl EditorElement {
                     bottom: false,
                     left: true,
                 };
-                let mut push_region = |start, end| {
-                    if let (Some(start_display), Some(end_display)) = (start, end) {
-                        let start_y = y_for_row(start_display as f32);
-                        let mut end_y = y_for_row(end_display as f32);
-                        if end_y - start_y < 1. {
-                            end_y = start_y + 1.;
-                        }
-                        let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y));
-
-                        scene.push_quad(Quad {
-                            bounds,
-                            background: Some(color),
-                            border,
-                            corner_radius: style.thumb.corner_radius,
-                        })
+                let mut push_region = |start: u32, end: u32| {
+                    let start_y = y_for_row(start as f32);
+                    let mut end_y = y_for_row(end as f32);
+                    if end_y - start_y < 1. {
+                        end_y = start_y + 1.;
                     }
+                    let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y));
+
+                    scene.push_quad(Quad {
+                        bounds,
+                        background: Some(color),
+                        border,
+                        corner_radius: style.thumb.corner_radius,
+                    })
                 };
-                for (row, _) in &editor
-                    .background_highlights_in_range_for::<crate::items::BufferSearchHighlights>(
+                let start = std::time::Instant::now();
+
+                let background_ranges = editor
+                    .selected_rows::<crate::items::BufferSearchHighlights>(
                         start_anchor..end_anchor,
                         &layout.position_map.snapshot,
                         &theme,
-                    )
-                {
-                    let start_display = row.start;
-                    let end_display = row.end;
-
-                    if start_row.is_none() {
-                        assert_eq!(end_row, None);
-                        start_row = Some(start_display.row());
-                        end_row = Some(end_display.row());
-                        continue;
-                    }
-                    if let Some(current_end) = end_row.as_mut() {
-                        if start_display.row() > *current_end + 1 {
-                            push_region(start_row, end_row);
-                            start_row = Some(start_display.row());
-                            end_row = Some(end_display.row());
-                        } else {
-                            // Merge two hunks.
-                            *current_end = end_display.row();
-                        }
-                    } else {
-                        unreachable!();
-                    }
+                    );
+                dbg!(start.elapsed().as_millis());
+                for row in background_ranges {
+                    let start = row.start();
+                    let end = row.end();
+                    push_region(*start, *end);
                 }
-                // We might still have a hunk that was not rendered (if there was a search hit on the last line)
-                push_region(start_row, end_row);
             }
 
             if layout.is_singleton && scrollbar_settings.git_diff {