diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 3313fb100b..3fd0acd753 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1748,6 +1748,15 @@ pub(crate) struct FocusedBlock { focus_handle: WeakFocusHandle, } +#[derive(Clone)] +struct JumpData { + excerpt_id: ExcerptId, + position: Point, + anchor: text::Anchor, + path: Option, + line_offset_from_top: u32, +} + impl Editor { pub fn single_line(cx: &mut ViewContext) -> Self { let buffer = cx.new_model(|cx| Buffer::local("", cx)); @@ -12584,47 +12593,83 @@ impl Editor { } pub fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext) { - self.open_excerpts_common(true, cx) + self.open_excerpts_common(None, true, cx) } pub fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext) { - self.open_excerpts_common(false, cx) + self.open_excerpts_common(None, false, cx) } - fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext) { - let selections = self.selections.all::(cx); - let buffer = self.buffer.read(cx); - if buffer.is_singleton() { - cx.propagate(); - return; - } - + fn open_excerpts_common( + &mut self, + jump_data: Option, + split: bool, + cx: &mut ViewContext, + ) { let Some(workspace) = self.workspace() else { cx.propagate(); return; }; - let mut new_selections_by_buffer = HashMap::default(); - for selection in selections { - for (mut buffer_handle, mut range, _) in - buffer.range_to_buffer_ranges(selection.range(), cx) - { - // When editing branch buffers, jump to the corresponding location - // in their base buffer. - let buffer = buffer_handle.read(cx); - if let Some(base_buffer) = buffer.diff_base_buffer() { - range = buffer.range_to_version(range, &base_buffer.read(cx).version()); - buffer_handle = base_buffer; - } + if self.buffer.read(cx).is_singleton() { + cx.propagate(); + return; + } - if selection.reversed { - mem::swap(&mut range.start, &mut range.end); + let mut new_selections_by_buffer = HashMap::default(); + match &jump_data { + Some(jump_data) => { + let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx); + if let Some(buffer) = multi_buffer_snapshot + .buffer_id_for_excerpt(jump_data.excerpt_id) + .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id)) + { + let buffer_snapshot = buffer.read(cx).snapshot(); + let jump_to_point = if buffer_snapshot.can_resolve(&jump_data.anchor) { + language::ToPoint::to_point(&jump_data.anchor, &buffer_snapshot) + } else { + buffer_snapshot.clip_point(jump_data.position, Bias::Left) + }; + let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point); + new_selections_by_buffer.insert( + buffer, + ( + vec![jump_to_offset..jump_to_offset], + Some(jump_data.line_offset_from_top), + ), + ); } - new_selections_by_buffer - .entry(buffer_handle) - .or_insert(Vec::new()) - .push(range) } + None => { + let selections = self.selections.all::(cx); + let buffer = self.buffer.read(cx); + for selection in selections { + for (mut buffer_handle, mut range, _) in + buffer.range_to_buffer_ranges(selection.range(), cx) + { + // When editing branch buffers, jump to the corresponding location + // in their base buffer. + let buffer = buffer_handle.read(cx); + if let Some(base_buffer) = buffer.diff_base_buffer() { + range = buffer.range_to_version(range, &base_buffer.read(cx).version()); + buffer_handle = base_buffer; + } + + if selection.reversed { + mem::swap(&mut range.start, &mut range.end); + } + new_selections_by_buffer + .entry(buffer_handle) + .or_insert((Vec::new(), None)) + .0 + .push(range) + } + } + } + } + + if new_selections_by_buffer.is_empty() { + return; } // We defer the pane interaction because we ourselves are a workspace item @@ -12638,13 +12683,19 @@ impl Editor { workspace.active_pane().clone() }; - for (buffer, ranges) in new_selections_by_buffer { + for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer { let editor = workspace.open_project_item::(pane.clone(), buffer, true, true, cx); editor.update(cx, |editor, cx| { - editor.change_selections(Some(Autoscroll::newest()), cx, |s| { + let autoscroll = match scroll_offset { + Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize), + None => Autoscroll::newest(), + }; + let nav_history = editor.nav_history.take(); + editor.change_selections(Some(autoscroll), cx, |s| { s.select_ranges(ranges); }); + editor.nav_history = nav_history; }); } }) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 594be60eaa..4323ea0ad7 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -19,8 +19,8 @@ use crate::{ BlockId, CodeActionsMenu, CursorShape, CustomBlockId, DisplayPoint, DisplayRow, DocumentHighlightRead, DocumentHighlightWrite, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, ExpandExcerpts, FocusedBlock, GutterDimensions, HalfPageDown, - HalfPageUp, HandleInput, HoveredCursor, HoveredHunk, LineDown, LineUp, OpenExcerpts, PageDown, - PageUp, Point, RowExt, RowRangeExt, SelectPhase, Selection, SoftWrap, ToPoint, + HalfPageUp, HandleInput, HoveredCursor, HoveredHunk, JumpData, LineDown, LineUp, OpenExcerpts, + PageDown, PageUp, Point, RowExt, RowRangeExt, SelectPhase, Selection, SoftWrap, ToPoint, CURSORS_VISIBLE_FOR, FILE_HEADER_HEIGHT, GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT, }; @@ -2058,6 +2058,7 @@ impl EditorElement { block: &Block, available_width: AvailableSpace, block_id: BlockId, + block_row_start: DisplayRow, snapshot: &EditorSnapshot, text_x: Pixels, rows: &Range, @@ -2129,14 +2130,9 @@ impl EditorElement { next_excerpt, show_excerpt_controls, starts_new_buffer, + height, .. } => { - #[derive(Clone)] - struct JumpData { - position: Point, - path: Option, - } - let icon_offset = gutter_dimensions.width - (gutter_dimensions.left_padding + gutter_dimensions.margin); @@ -2175,9 +2171,28 @@ impl EditorElement { .primary .as_ref() .map_or(range.context.start, |primary| primary.start); + + let excerpt_start = range.context.start; + let jump_position = language::ToPoint::to_point(&jump_anchor, buffer); + let offset_from_excerpt_start = if jump_anchor == excerpt_start { + 0 + } else { + let excerpt_start_row = + language::ToPoint::to_point(&jump_anchor, buffer).row; + jump_position.row - excerpt_start_row + }; + let line_offset_from_top = + block_row_start.0 + *height + offset_from_excerpt_start + - snapshot + .scroll_anchor + .scroll_position(&snapshot.display_snapshot) + .y as u32; JumpData { + excerpt_id: next_excerpt.id, + anchor: jump_anchor, position: language::ToPoint::to_point(&jump_anchor, buffer), path: jump_path, + line_offset_from_top, } }; @@ -2245,6 +2260,7 @@ impl EditorElement { .on_click(cx.listener_for(&self.editor, { move |editor, e: &ClickEvent, cx| { editor.open_excerpts_common( + Some(jump_data.clone()), e.down.modifiers.secondary(), cx, ); @@ -2289,39 +2305,51 @@ impl EditorElement { }), ) .cursor_pointer() - .on_click(cx.listener_for(&self.editor, { - move |editor, e: &ClickEvent, cx| { - cx.stop_propagation(); - editor - .open_excerpts_common(e.down.modifiers.secondary(), cx); + .on_click({ + let jump_data = jump_data.clone(); + cx.listener_for(&self.editor, { + let jump_data = jump_data.clone(); + move |editor, e: &ClickEvent, cx| { + cx.stop_propagation(); + editor.open_excerpts_common( + Some(jump_data.clone()), + e.down.modifiers.secondary(), + cx, + ); + } + }) + }) + .tooltip({ + let jump_data = jump_data.clone(); + move |cx| { + let jump_message = format!( + "Jump to {}:L{}", + match &jump_data.path { + Some(project_path) => + project_path.path.display().to_string(), + None => { + let editor = editor.read(cx); + editor + .file_at(jump_data.position, cx) + .map(|file| { + file.full_path(cx).display().to_string() + }) + .or_else(|| { + Some( + editor + .tab_description(0, cx)? + .to_string(), + ) + }) + .unwrap_or_else(|| { + "Unknown buffer".to_string() + }) + } + }, + jump_data.position.row + 1 + ); + Tooltip::for_action(jump_message, &OpenExcerpts, cx) } - })) - .tooltip(move |cx| { - let jump_message = format!( - "Jump to {}:L{}", - match &jump_data.path { - Some(project_path) => - project_path.path.display().to_string(), - None => { - let editor = editor.read(cx); - editor - .file_at(jump_data.position, cx) - .map(|file| { - file.full_path(cx).display().to_string() - }) - .or_else(|| { - Some( - editor - .tab_description(0, cx)? - .to_string(), - ) - }) - .unwrap_or_else(|| "Unknown buffer".to_string()) - } - }, - jump_data.position.row + 1 - ); - Tooltip::for_action(jump_message, &OpenExcerpts, cx) }) .child( h_flex() @@ -2456,6 +2484,7 @@ impl EditorElement { block, AvailableSpace::MinContent, block_id, + row, snapshot, text_x, &rows, @@ -2501,6 +2530,7 @@ impl EditorElement { block, width.into(), block_id, + row, snapshot, text_x, &rows, @@ -2547,6 +2577,7 @@ impl EditorElement { &block, width, focused_block.id, + rows.end, snapshot, text_x, &rows,