Fix excerpt jumps using selections, not the match data (#20491)
Fixes https://github.com/zed-industries/zed/pull/20469#issuecomment-2466805325 Release Notes: - N/A --------- Co-authored-by: Bennet <bennet@zed.dev>
This commit is contained in:
parent
1460249a70
commit
f4024cc602
2 changed files with 152 additions and 70 deletions
|
@ -1748,6 +1748,15 @@ pub(crate) struct FocusedBlock {
|
||||||
focus_handle: WeakFocusHandle,
|
focus_handle: WeakFocusHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct JumpData {
|
||||||
|
excerpt_id: ExcerptId,
|
||||||
|
position: Point,
|
||||||
|
anchor: text::Anchor,
|
||||||
|
path: Option<project::ProjectPath>,
|
||||||
|
line_offset_from_top: u32,
|
||||||
|
}
|
||||||
|
|
||||||
impl Editor {
|
impl Editor {
|
||||||
pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
|
pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
|
||||||
let buffer = cx.new_model(|cx| Buffer::local("", cx));
|
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>) {
|
pub fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
|
||||||
self.open_excerpts_common(true, cx)
|
self.open_excerpts_common(None, true, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
|
pub fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
|
||||||
self.open_excerpts_common(false, cx)
|
self.open_excerpts_common(None, false, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
|
fn open_excerpts_common(
|
||||||
let selections = self.selections.all::<usize>(cx);
|
&mut self,
|
||||||
let buffer = self.buffer.read(cx);
|
jump_data: Option<JumpData>,
|
||||||
if buffer.is_singleton() {
|
split: bool,
|
||||||
cx.propagate();
|
cx: &mut ViewContext<Self>,
|
||||||
return;
|
) {
|
||||||
}
|
|
||||||
|
|
||||||
let Some(workspace) = self.workspace() else {
|
let Some(workspace) = self.workspace() else {
|
||||||
cx.propagate();
|
cx.propagate();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut new_selections_by_buffer = HashMap::default();
|
if self.buffer.read(cx).is_singleton() {
|
||||||
for selection in selections {
|
cx.propagate();
|
||||||
for (mut buffer_handle, mut range, _) in
|
return;
|
||||||
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 {
|
let mut new_selections_by_buffer = HashMap::default();
|
||||||
mem::swap(&mut range.start, &mut range.end);
|
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::<usize>(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
|
// We defer the pane interaction because we ourselves are a workspace item
|
||||||
|
@ -12638,13 +12683,19 @@ impl Editor {
|
||||||
workspace.active_pane().clone()
|
workspace.active_pane().clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
for (buffer, ranges) in new_selections_by_buffer {
|
for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
|
||||||
let editor =
|
let editor =
|
||||||
workspace.open_project_item::<Self>(pane.clone(), buffer, true, true, cx);
|
workspace.open_project_item::<Self>(pane.clone(), buffer, true, true, cx);
|
||||||
editor.update(cx, |editor, 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);
|
s.select_ranges(ranges);
|
||||||
});
|
});
|
||||||
|
editor.nav_history = nav_history;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -19,8 +19,8 @@ use crate::{
|
||||||
BlockId, CodeActionsMenu, CursorShape, CustomBlockId, DisplayPoint, DisplayRow,
|
BlockId, CodeActionsMenu, CursorShape, CustomBlockId, DisplayPoint, DisplayRow,
|
||||||
DocumentHighlightRead, DocumentHighlightWrite, Editor, EditorMode, EditorSettings,
|
DocumentHighlightRead, DocumentHighlightWrite, Editor, EditorMode, EditorSettings,
|
||||||
EditorSnapshot, EditorStyle, ExpandExcerpts, FocusedBlock, GutterDimensions, HalfPageDown,
|
EditorSnapshot, EditorStyle, ExpandExcerpts, FocusedBlock, GutterDimensions, HalfPageDown,
|
||||||
HalfPageUp, HandleInput, HoveredCursor, HoveredHunk, LineDown, LineUp, OpenExcerpts, PageDown,
|
HalfPageUp, HandleInput, HoveredCursor, HoveredHunk, JumpData, LineDown, LineUp, OpenExcerpts,
|
||||||
PageUp, Point, RowExt, RowRangeExt, SelectPhase, Selection, SoftWrap, ToPoint,
|
PageDown, PageUp, Point, RowExt, RowRangeExt, SelectPhase, Selection, SoftWrap, ToPoint,
|
||||||
CURSORS_VISIBLE_FOR, FILE_HEADER_HEIGHT, GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN,
|
CURSORS_VISIBLE_FOR, FILE_HEADER_HEIGHT, GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN,
|
||||||
MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
|
MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
|
||||||
};
|
};
|
||||||
|
@ -2058,6 +2058,7 @@ impl EditorElement {
|
||||||
block: &Block,
|
block: &Block,
|
||||||
available_width: AvailableSpace,
|
available_width: AvailableSpace,
|
||||||
block_id: BlockId,
|
block_id: BlockId,
|
||||||
|
block_row_start: DisplayRow,
|
||||||
snapshot: &EditorSnapshot,
|
snapshot: &EditorSnapshot,
|
||||||
text_x: Pixels,
|
text_x: Pixels,
|
||||||
rows: &Range<DisplayRow>,
|
rows: &Range<DisplayRow>,
|
||||||
|
@ -2129,14 +2130,9 @@ impl EditorElement {
|
||||||
next_excerpt,
|
next_excerpt,
|
||||||
show_excerpt_controls,
|
show_excerpt_controls,
|
||||||
starts_new_buffer,
|
starts_new_buffer,
|
||||||
|
height,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
#[derive(Clone)]
|
|
||||||
struct JumpData {
|
|
||||||
position: Point,
|
|
||||||
path: Option<ProjectPath>,
|
|
||||||
}
|
|
||||||
|
|
||||||
let icon_offset = gutter_dimensions.width
|
let icon_offset = gutter_dimensions.width
|
||||||
- (gutter_dimensions.left_padding + gutter_dimensions.margin);
|
- (gutter_dimensions.left_padding + gutter_dimensions.margin);
|
||||||
|
|
||||||
|
@ -2175,9 +2171,28 @@ impl EditorElement {
|
||||||
.primary
|
.primary
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(range.context.start, |primary| primary.start);
|
.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 {
|
JumpData {
|
||||||
|
excerpt_id: next_excerpt.id,
|
||||||
|
anchor: jump_anchor,
|
||||||
position: language::ToPoint::to_point(&jump_anchor, buffer),
|
position: language::ToPoint::to_point(&jump_anchor, buffer),
|
||||||
path: jump_path,
|
path: jump_path,
|
||||||
|
line_offset_from_top,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2245,6 +2260,7 @@ impl EditorElement {
|
||||||
.on_click(cx.listener_for(&self.editor, {
|
.on_click(cx.listener_for(&self.editor, {
|
||||||
move |editor, e: &ClickEvent, cx| {
|
move |editor, e: &ClickEvent, cx| {
|
||||||
editor.open_excerpts_common(
|
editor.open_excerpts_common(
|
||||||
|
Some(jump_data.clone()),
|
||||||
e.down.modifiers.secondary(),
|
e.down.modifiers.secondary(),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
@ -2289,39 +2305,51 @@ impl EditorElement {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.cursor_pointer()
|
.cursor_pointer()
|
||||||
.on_click(cx.listener_for(&self.editor, {
|
.on_click({
|
||||||
move |editor, e: &ClickEvent, cx| {
|
let jump_data = jump_data.clone();
|
||||||
cx.stop_propagation();
|
cx.listener_for(&self.editor, {
|
||||||
editor
|
let jump_data = jump_data.clone();
|
||||||
.open_excerpts_common(e.down.modifiers.secondary(), cx);
|
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(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
|
@ -2456,6 +2484,7 @@ impl EditorElement {
|
||||||
block,
|
block,
|
||||||
AvailableSpace::MinContent,
|
AvailableSpace::MinContent,
|
||||||
block_id,
|
block_id,
|
||||||
|
row,
|
||||||
snapshot,
|
snapshot,
|
||||||
text_x,
|
text_x,
|
||||||
&rows,
|
&rows,
|
||||||
|
@ -2501,6 +2530,7 @@ impl EditorElement {
|
||||||
block,
|
block,
|
||||||
width.into(),
|
width.into(),
|
||||||
block_id,
|
block_id,
|
||||||
|
row,
|
||||||
snapshot,
|
snapshot,
|
||||||
text_x,
|
text_x,
|
||||||
&rows,
|
&rows,
|
||||||
|
@ -2547,6 +2577,7 @@ impl EditorElement {
|
||||||
&block,
|
&block,
|
||||||
width,
|
width,
|
||||||
focused_block.id,
|
focused_block.id,
|
||||||
|
rows.end,
|
||||||
snapshot,
|
snapshot,
|
||||||
text_x,
|
text_x,
|
||||||
&rows,
|
&rows,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue