Make scrollbar content detection cheaper

Mikayla Maki and max created

Remove scrollbars from multibuffers

co-authored-by: max <max@zed.dev>

Change summary

crates/editor/src/editor.rs       |  8 +-
crates/editor/src/element.rs      | 93 +++++++++++++++++---------------
crates/editor/src/multi_buffer.rs |  9 +++
crates/git/src/diff.rs            |  4 +
4 files changed, 65 insertions(+), 49 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -526,11 +526,9 @@ pub struct EditorSnapshot {
 }
 
 impl EditorSnapshot {
-    fn has_scrollbar_info(&self) -> bool {
-        self.buffer_snapshot
-            .git_diff_hunks_in_range(0..self.max_point().row())
-            .next()
-            .is_some()
+    fn has_scrollbar_info(&self, is_singleton: bool) -> bool {
+        is_singleton && self.buffer_snapshot
+            .has_git_diffs()
     }
 }
 

crates/editor/src/element.rs 🔗

@@ -1052,51 +1052,53 @@ impl EditorElement {
                 ..Default::default()
             });
 
-            let diff_style = theme::current(cx).editor.diff.clone();
-            for hunk in layout
-                .position_map
-                .snapshot
-                .buffer_snapshot
-                .git_diff_hunks_in_range(0..(max_row.floor() as u32))
-            {
-                let start_display = Point::new(hunk.buffer_range.start, 0)
-                    .to_display_point(&layout.position_map.snapshot.display_snapshot);
-                let end_display = Point::new(hunk.buffer_range.end, 0)
-                    .to_display_point(&layout.position_map.snapshot.display_snapshot);
-                let start_y = y_for_row(start_display.row() as f32);
-                let mut end_y = if hunk.buffer_range.start == hunk.buffer_range.end {
-                    y_for_row((end_display.row() + 1) as f32)
-                } else {
-                    y_for_row((end_display.row()) as f32)
-                };
+            if layout.is_singleton {
+                let diff_style = theme::current(cx).editor.diff.clone();
+                for hunk in layout
+                    .position_map
+                    .snapshot
+                    .buffer_snapshot
+                    .git_diff_hunks_in_range(0..(max_row.floor() as u32))
+                {
+                    let start_display = Point::new(hunk.buffer_range.start, 0)
+                        .to_display_point(&layout.position_map.snapshot.display_snapshot);
+                    let end_display = Point::new(hunk.buffer_range.end, 0)
+                        .to_display_point(&layout.position_map.snapshot.display_snapshot);
+                    let start_y = y_for_row(start_display.row() as f32);
+                    let mut end_y = if hunk.buffer_range.start == hunk.buffer_range.end {
+                        y_for_row((end_display.row() + 1) as f32)
+                    } else {
+                        y_for_row((end_display.row()) 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));
-
-                let color = match hunk.status() {
-                    DiffHunkStatus::Added => diff_style.inserted,
-                    DiffHunkStatus::Modified => diff_style.modified,
-                    DiffHunkStatus::Removed => diff_style.deleted,
-                };
-
-                let border = Border {
-                    width: 1.,
-                    color: style.thumb.border.color,
-                    overlay: false,
-                    top: false,
-                    right: true,
-                    bottom: false,
-                    left: true,
-                };
+                    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 color = match hunk.status() {
+                        DiffHunkStatus::Added => diff_style.inserted,
+                        DiffHunkStatus::Modified => diff_style.modified,
+                        DiffHunkStatus::Removed => diff_style.deleted,
+                    };
+
+                    let border = Border {
+                        width: 1.,
+                        color: style.thumb.border.color,
+                        overlay: false,
+                        top: false,
+                        right: true,
+                        bottom: false,
+                        left: true,
+                    };
+
+                    scene.push_quad(Quad {
+                        bounds,
+                        background: Some(color),
+                        border,
+                        corner_radius: style.thumb.corner_radius,
+                    })
+                }
             }
 
             scene.push_quad(Quad {
@@ -2067,7 +2069,8 @@ impl Element<Editor> for EditorElement {
 
         let show_scrollbars = match settings::get::<EditorSettings>(cx).show_scrollbars {
             ShowScrollbars::Auto => {
-                snapshot.has_scrollbar_info() || editor.scroll_manager.scrollbars_visible()
+                snapshot.has_scrollbar_info(is_singleton)
+                    || editor.scroll_manager.scrollbars_visible()
             }
             ShowScrollbars::System => editor.scroll_manager.scrollbars_visible(),
             ShowScrollbars::Always => true,
@@ -2290,6 +2293,7 @@ impl Element<Editor> for EditorElement {
                 text_size,
                 scrollbar_row_range,
                 show_scrollbars,
+                is_singleton,
                 max_row,
                 gutter_margin,
                 active_rows,
@@ -2445,6 +2449,7 @@ pub struct LayoutState {
     selections: Vec<(ReplicaId, Vec<SelectionLayout>)>,
     scrollbar_row_range: Range<f32>,
     show_scrollbars: bool,
+    is_singleton: bool,
     max_row: u32,
     context_menu: Option<(DisplayPoint, AnyElement<Editor>)>,
     code_actions_indicator: Option<(u32, AnyElement<Editor>)>,

crates/editor/src/multi_buffer.rs 🔗

@@ -2841,6 +2841,15 @@ impl MultiBufferSnapshot {
             })
     }
 
+    pub fn has_git_diffs(&self) -> bool {
+        for excerpt in self.excerpts.iter() {
+            if !excerpt.buffer.git_diff.is_empty() {
+                return true;
+            }
+        }
+        false
+    }
+
     pub fn git_diff_hunks_in_range_rev<'a>(
         &'a self,
         row_range: Range<u32>,

crates/git/src/diff.rs 🔗

@@ -71,6 +71,10 @@ impl BufferDiff {
         }
     }
 
+    pub fn is_empty(&self) -> bool {
+        self.tree.is_empty()
+    }
+
     pub fn hunks_in_row_range<'a>(
         &'a self,
         range: Range<u32>,