Fix vertical alignment when jumping from multibuffers (#22613)

Agus Zubiaga created

Clicking buffer headers and line numbers would sometimes take you to a
disorienting scroll position. This PR improves that so the destination
line is roughly at the same Y position as it appeared in the
multibuffer.



https://github.com/user-attachments/assets/3ad71537-cf26-4136-948f-c5a96df57178


**Note**: The alignment won't always be perfect because the multibuffer
and target buffer might start at a different absolute Y position
(because of open search, breadcrumbs, etc). I wanted to compensate for
that, but that requires a fundamental change that I'd prefer to make
separately.

Release Notes:

- Fix vertical alignment when jumping from multibuffers

Change summary

crates/editor/src/editor.rs  | 12 +++++++++---
crates/editor/src/element.rs | 23 +++++++++++++++++------
2 files changed, 26 insertions(+), 9 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -993,7 +993,10 @@ pub(crate) struct FocusedBlock {
 
 #[derive(Clone)]
 enum JumpData {
-    MultiBufferRow(MultiBufferRow),
+    MultiBufferRow {
+        row: MultiBufferRow,
+        line_offset_from_top: u32,
+    },
     MultiBufferPoint {
         excerpt_id: ExcerptId,
         position: Point,
@@ -12487,7 +12490,10 @@ impl Editor {
                     );
                 }
             }
-            Some(JumpData::MultiBufferRow(row)) => {
+            Some(JumpData::MultiBufferRow {
+                row,
+                line_offset_from_top,
+            }) => {
                 let point = MultiBufferPoint::new(row.0, 0);
                 if let Some((buffer, buffer_point, _)) =
                     self.buffer.read(cx).point_to_buffer_point(point, cx)
@@ -12495,7 +12501,7 @@ impl Editor {
                     let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
                     new_selections_by_buffer
                         .entry(buffer)
-                        .or_insert((Vec::new(), None))
+                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
                         .0
                         .push(buffer_offset..buffer_offset)
                 }

crates/editor/src/element.rs 🔗

@@ -599,8 +599,15 @@ impl EditorElement {
                 .row;
             if let Some((_, Some(hitbox))) = line_numbers.get(&MultiBufferRow(multi_buffer_row)) {
                 if hitbox.contains(&event.position) {
+                    let scroll_position_row =
+                        position_map.scroll_pixel_position.y / position_map.line_height;
+                    let line_offset_from_top = display_row - scroll_position_row as u32;
+
                     editor.open_excerpts_common(
-                        Some(JumpData::MultiBufferRow(MultiBufferRow(multi_buffer_row))),
+                        Some(JumpData::MultiBufferRow {
+                            row: MultiBufferRow(multi_buffer_row),
+                            line_offset_from_top,
+                        }),
                         modifiers.alt,
                         cx,
                     );
@@ -2959,7 +2966,12 @@ impl EditorElement {
         selected_buffer_ids: &Vec<BufferId>,
         cx: &mut WindowContext,
     ) -> AnyElement {
-        let jump_data = header_jump_data(snapshot, DisplayRow(0), FILE_HEADER_HEIGHT, excerpt);
+        let jump_data = header_jump_data(
+            snapshot,
+            DisplayRow(scroll_position as u32),
+            FILE_HEADER_HEIGHT + MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
+            excerpt,
+        );
 
         let editor_bg_color = cx.theme().colors().editor_background;
 
@@ -5121,13 +5133,12 @@ fn header_jump_data(
     let offset_from_excerpt_start = if jump_anchor == excerpt_start {
         0
     } else {
-        let excerpt_start_row = language::ToPoint::to_point(&jump_anchor, buffer).row;
+        let excerpt_start_row = language::ToPoint::to_point(&excerpt_start, buffer).row;
         jump_position.row - excerpt_start_row
     };
 
-    let line_offset_from_top = block_row_start.0
-        + height
-        + offset_from_excerpt_start.saturating_sub(
+    let line_offset_from_top = (block_row_start.0 + height + offset_from_excerpt_start)
+        .saturating_sub(
             snapshot
                 .scroll_anchor
                 .scroll_position(&snapshot.display_snapshot)