From 0214d6e78b6a72577047da2aebb41187eaa5418d Mon Sep 17 00:00:00 2001 From: KyleBarton Date: Mon, 2 Mar 2026 08:09:13 -0800 Subject: [PATCH] Adjust scroll top by number of sticky headers (#50359) In cases where sticky headers are enabled, count the number of sticky headers that would be present when performing `editor:scroll cursor top`. Take the maximum of that number and `verical_scroll_margin` so that we don't inadvertently bury the cursor behind the sticky headers. https://github.com/user-attachments/assets/6d49fe3a-2017-4c76-bd92-c4ec9794f898 Closes #48864 Before you mark this PR as ready for review, make sure that you have: - [x] Added a solid test coverage and/or screenshots from doing manual testing - [x] Done a self-review taking into account security and performance aspects - [x] Aligned any UI changes with the [UI checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) Release Notes: - Fixed scroll top behavior when there are more sticky headers than vertical_scroll_margin --- crates/editor/src/scroll/actions.rs | 37 ++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/crates/editor/src/scroll/actions.rs b/crates/editor/src/scroll/actions.rs index 5a1c849b2438fe987b24481b824375e188468916..3d22db2a4dc3c9339e51b0dae02d6d598400ad64 100644 --- a/crates/editor/src/scroll/actions.rs +++ b/crates/editor/src/scroll/actions.rs @@ -1,10 +1,12 @@ use super::Axis; use crate::{ - Autoscroll, Editor, EditorMode, NextScreen, NextScrollCursorCenterTopBottom, + Autoscroll, Editor, EditorMode, EditorSettings, NextScreen, NextScrollCursorCenterTopBottom, SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT, ScrollCursorBottom, ScrollCursorCenter, ScrollCursorCenterTopBottom, ScrollCursorTop, display_map::DisplayRow, scroll::ScrollOffset, }; use gpui::{Context, Point, Window}; +use settings::Settings; +use text::ToOffset; impl Editor { pub fn next_screen(&mut self, _: &NextScreen, window: &mut Window, cx: &mut Context) { @@ -73,18 +75,37 @@ impl Editor { ) { let display_snapshot = self.display_snapshot(cx); let scroll_margin_rows = self.vertical_scroll_margin() as u32; - let new_screen_top = self - .selections - .newest_display(&display_snapshot) - .head() - .row() - .0; + let selection_head = self.selections.newest_display(&display_snapshot).head(); + + let sticky_headers_len = if EditorSettings::get_global(cx).sticky_scroll.enabled + && let Some((_, _, buffer_snapshot)) = display_snapshot.buffer_snapshot().as_singleton() + { + let select_head_point = + rope::Point::new(selection_head.to_point(&display_snapshot).row, 0); + buffer_snapshot + .outline_items_containing(select_head_point..select_head_point, false, None) + .iter() + .filter(|outline| { + outline.range.start.offset + < select_head_point.to_offset(&buffer_snapshot) as u32 + }) + .collect::>() + .len() + } else { + 0 + } as u32; + + let new_screen_top = selection_head.row().0; let header_offset = display_snapshot .buffer_snapshot() .show_headers() .then(|| display_snapshot.buffer_header_height()) .unwrap_or(0); - let new_screen_top = new_screen_top.saturating_sub(scroll_margin_rows + header_offset); + + // If the number of sticky headers exceeds the vertical_scroll_margin, + // we need to adjust the scroll top a bit further + let adjustment = scroll_margin_rows.max(sticky_headers_len) + header_offset; + let new_screen_top = new_screen_top.saturating_sub(adjustment); self.set_scroll_top_row(DisplayRow(new_screen_top), window, cx); }