From b4b7a23c39b3339d09c307c386913713f4368a49 Mon Sep 17 00:00:00 2001 From: Smit Barmase Date: Wed, 12 Nov 2025 02:04:37 +0530 Subject: [PATCH] editor: Improve multi-buffer header filename click to jump to the latest selection from that buffer (#42480) Closes https://github.com/zed-industries/zed/pull/42099 Regressed in https://github.com/zed-industries/zed/pull/42283 Release Notes: - Clicking the multi-buffer header file name or the "Open file" button now jumps to the most recent selection in that buffer, if one exists. --- crates/editor/src/element.rs | 145 ++++++++++++++++++++++++----------- 1 file changed, 102 insertions(+), 43 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 7442ccc7442a11ab2f845cc637e5ad416085af02..6fd259dae9333933fa7f29041c2deb591b42bf6d 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -6,10 +6,10 @@ use crate::{ EditDisplayMode, EditPrediction, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, FILE_HEADER_HEIGHT, FocusedBlock, GutterDimensions, HalfPageDown, HalfPageUp, HandleInput, HoveredCursor, InlayHintRefreshReason, JumpData, LineDown, LineHighlight, LineUp, - MAX_LINE_LEN, MINIMAP_FONT_SIZE, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT, OpenExcerpts, - OpenExcerptsSplit, PageDown, PageUp, PhantomBreakpointIndicator, Point, RowExt, RowRangeExt, - SelectPhase, SelectedTextHighlight, Selection, SelectionDragState, SelectionEffects, - SizingBehavior, SoftWrap, StickyHeaderExcerpt, ToPoint, ToggleFold, ToggleFoldAll, + MAX_LINE_LEN, MINIMAP_FONT_SIZE, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT, OpenExcerpts, PageDown, + PageUp, PhantomBreakpointIndicator, Point, RowExt, RowRangeExt, SelectPhase, + SelectedTextHighlight, Selection, SelectionDragState, SelectionEffects, SizingBehavior, + SoftWrap, StickyHeaderExcerpt, ToPoint, ToggleFold, ToggleFoldAll, code_context_menus::{CodeActionsMenu, MENU_ASIDE_MAX_WIDTH, MENU_ASIDE_MIN_WIDTH, MENU_GAP}, display_map::{ Block, BlockContext, BlockStyle, ChunkRendererId, DisplaySnapshot, EditorMargins, @@ -3664,6 +3664,7 @@ impl EditorElement { row_block_types: &mut HashMap, selections: &[Selection], selected_buffer_ids: &Vec, + selection_anchors: &[Selection], is_row_soft_wrapped: impl Copy + Fn(usize) -> bool, sticky_header_excerpt_id: Option, window: &mut Window, @@ -3739,7 +3740,13 @@ impl EditorElement { let selected = selected_buffer_ids.contains(&first_excerpt.buffer_id); let result = v_flex().id(block_id).w_full().pr(editor_margins.right); - let jump_data = header_jump_data(snapshot, block_row_start, *height, first_excerpt); + let jump_data = header_jump_data( + snapshot, + block_row_start, + *height, + first_excerpt, + selection_anchors, + ); result .child(self.render_buffer_header( first_excerpt, @@ -3774,7 +3781,13 @@ impl EditorElement { Block::BufferHeader { excerpt, height } => { let mut result = v_flex().id(block_id).w_full(); - let jump_data = header_jump_data(snapshot, block_row_start, *height, excerpt); + let jump_data = header_jump_data( + snapshot, + block_row_start, + *height, + excerpt, + selection_anchors, + ); if sticky_header_excerpt_id != Some(excerpt.id) { let selected = selected_buffer_ids.contains(&excerpt.buffer_id); @@ -4042,24 +4055,18 @@ impl EditorElement { ) .group_hover("", |div| div.underline()), ) - .on_click({ - let focus_handle = focus_handle.clone(); - move |event, window, cx| { - if event.modifiers().secondary() { - focus_handle.dispatch_action( - &OpenExcerptsSplit, - window, - cx, - ); - } else { - focus_handle.dispatch_action( - &OpenExcerpts, - window, - cx, - ); - } + .on_click(window.listener_for(&self.editor, { + let jump_data = jump_data.clone(); + + move |editor, e: &ClickEvent, window, cx| { + editor.open_excerpts_common( + Some(jump_data.clone()), + e.modifiers().secondary(), + window, + cx, + ); } - }), + })), ) .when_some(parent_path, |then, path| { then.child(div().child(path).text_color( @@ -4087,24 +4094,18 @@ impl EditorElement { cx, )), ) - .on_click({ - let focus_handle = focus_handle.clone(); - move |event, window, cx| { - if event.modifiers().secondary() { - focus_handle.dispatch_action( - &OpenExcerptsSplit, - window, - cx, - ); - } else { - focus_handle.dispatch_action( - &OpenExcerpts, - window, - cx, - ); - } + .on_click(window.listener_for(&self.editor, { + let jump_data = jump_data.clone(); + + move |editor, e: &ClickEvent, window, cx| { + editor.open_excerpts_common( + Some(jump_data.clone()), + e.modifiers().secondary(), + window, + cx, + ); } - }), + })), ) }, ) @@ -4250,6 +4251,7 @@ impl EditorElement { line_layouts: &mut [LineWithInvisibles], selections: &[Selection], selected_buffer_ids: &Vec, + selection_anchors: &[Selection], is_row_soft_wrapped: impl Copy + Fn(usize) -> bool, sticky_header_excerpt_id: Option, window: &mut Window, @@ -4293,6 +4295,7 @@ impl EditorElement { &mut row_block_types, selections, selected_buffer_ids, + selection_anchors, is_row_soft_wrapped, sticky_header_excerpt_id, window, @@ -4350,6 +4353,7 @@ impl EditorElement { &mut row_block_types, selections, selected_buffer_ids, + selection_anchors, is_row_soft_wrapped, sticky_header_excerpt_id, window, @@ -4405,6 +4409,7 @@ impl EditorElement { &mut row_block_types, selections, selected_buffer_ids, + selection_anchors, is_row_soft_wrapped, sticky_header_excerpt_id, window, @@ -4487,6 +4492,7 @@ impl EditorElement { hitbox: &Hitbox, selected_buffer_ids: &Vec, blocks: &[BlockLayout], + selection_anchors: &[Selection], window: &mut Window, cx: &mut App, ) -> AnyElement { @@ -4495,6 +4501,7 @@ impl EditorElement { DisplayRow(scroll_position.y as u32), FILE_HEADER_HEIGHT + MULTI_BUFFER_EXCERPT_HEADER_HEIGHT, excerpt, + selection_anchors, ); let editor_bg_color = cx.theme().colors().editor_background; @@ -7798,7 +7805,34 @@ fn header_jump_data( block_row_start: DisplayRow, height: u32, for_excerpt: &ExcerptInfo, + selection_anchors: &[Selection], ) -> JumpData { + if let Some(cursor_anchor) = latest_anchor_for_buffer(selection_anchors, for_excerpt.buffer_id) + { + let buffer_point = + language::ToPoint::to_point(&cursor_anchor.text_anchor, &for_excerpt.buffer); + let multibuffer_point = snapshot + .buffer_snapshot() + .summary_for_anchor::(&cursor_anchor); + let display_row = snapshot + .display_snapshot + .point_to_display_point(multibuffer_point, Bias::Left) + .row() + .0; + let scroll_row = snapshot + .scroll_anchor + .scroll_position(&snapshot.display_snapshot) + .y as u32; + let line_offset_from_top = display_row.saturating_sub(scroll_row); + + return JumpData::MultiBufferPoint { + excerpt_id: cursor_anchor.excerpt_id, + anchor: cursor_anchor.text_anchor, + position: buffer_point, + line_offset_from_top, + }; + } + let range = &for_excerpt.range; let buffer = &for_excerpt.buffer; let jump_anchor = range.primary.start; @@ -7828,6 +7862,20 @@ fn header_jump_data( } } +fn latest_anchor_for_buffer( + selection_anchors: &[Selection], + buffer_id: BufferId, +) -> Option { + selection_anchors + .iter() + .filter_map(|selection| { + let head = selection.head(); + (head.buffer_id == Some(buffer_id)).then_some((selection.id, head)) + }) + .max_by_key(|(id, _)| *id) + .map(|(_, anchor)| anchor) +} + pub struct AcceptEditPredictionBinding(pub(crate) Option); impl AcceptEditPredictionBinding { @@ -9139,15 +9187,18 @@ impl Element for EditorElement { cx, ); - let (local_selections, selected_buffer_ids): ( + let (local_selections, selected_buffer_ids, selection_anchors): ( Vec>, Vec, + Arc<[Selection]>, ) = self .editor_with_selections(cx) .map(|editor| { editor.update(cx, |editor, cx| { let all_selections = editor.selections.all::(&snapshot.display_snapshot); + let all_anchor_selections = + editor.selections.all_anchors(&snapshot.display_snapshot); let selected_buffer_ids = if editor.buffer_kind(cx) == ItemBufferKind::Singleton { Vec::new() @@ -9176,10 +9227,16 @@ impl Element for EditorElement { selections .extend(editor.selections.pending(&snapshot.display_snapshot)); - (selections, selected_buffer_ids) + (selections, selected_buffer_ids, all_anchor_selections) }) }) - .unwrap_or_default(); + .unwrap_or_else(|| { + ( + Vec::new(), + Vec::new(), + Arc::<[Selection]>::from(Vec::new()), + ) + }); let (selections, mut active_rows, newest_selection_head) = self .layout_selections( @@ -9410,6 +9467,7 @@ impl Element for EditorElement { &mut line_layouts, &local_selections, &selected_buffer_ids, + selection_anchors.as_ref(), is_row_soft_wrapped, sticky_header_excerpt_id, window, @@ -9443,6 +9501,7 @@ impl Element for EditorElement { &hitbox, &selected_buffer_ids, &blocks, + selection_anchors.as_ref(), window, cx, )