multi_buffer: Make anchor_in_excerpt fallible for bad text anchors (#40496)
`MultiBuffer::anchor_in_excerpt` currently just wraps the given text anchor in a multibuffer anchor. This allows one to get a multibuffer anchor that points outside its excerpt which is basically never what one wants. This PR now does a bounds check and returns `None` if the given text anchor is not within the bounds of the excerpt. Release Notes: - N/A *or* Added/Fixed/Improved ... Co-authored-by: Kirill Bulatov <kirill@zed.dev>
This commit is contained in:
co-authored by
Kirill Bulatov
parent
7f9898a90b
commit
83bfe2ff7b
@@ -292,15 +292,10 @@ impl MessageEditor {
|
||||
let snapshot = self
|
||||
.editor
|
||||
.update(cx, |editor, cx| editor.snapshot(window, cx));
|
||||
let Some((excerpt_id, _, _)) = snapshot.buffer_snapshot().as_singleton() else {
|
||||
return Task::ready(());
|
||||
};
|
||||
let Some(start_anchor) = snapshot
|
||||
.buffer_snapshot()
|
||||
.anchor_in_excerpt(*excerpt_id, start)
|
||||
else {
|
||||
let Some(start_anchor) = snapshot.buffer_snapshot().as_singleton_anchor(start) else {
|
||||
return Task::ready(());
|
||||
};
|
||||
let excerpt_id = start_anchor.excerpt_id;
|
||||
let end_anchor = snapshot
|
||||
.buffer_snapshot()
|
||||
.anchor_before(start_anchor.to_offset(&snapshot.buffer_snapshot()) + content_len + 1);
|
||||
@@ -332,7 +327,7 @@ impl MessageEditor {
|
||||
})
|
||||
.shared();
|
||||
insert_crease_for_mention(
|
||||
*excerpt_id,
|
||||
excerpt_id,
|
||||
start,
|
||||
content_len,
|
||||
mention_uri.name().into(),
|
||||
@@ -344,7 +339,7 @@ impl MessageEditor {
|
||||
)
|
||||
} else {
|
||||
insert_crease_for_mention(
|
||||
*excerpt_id,
|
||||
excerpt_id,
|
||||
start,
|
||||
content_len,
|
||||
crease_text,
|
||||
@@ -546,10 +541,7 @@ impl MessageEditor {
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let snapshot = self.editor.read(cx).buffer().read(cx).snapshot(cx);
|
||||
let Some((&excerpt_id, _, _)) = snapshot.as_singleton() else {
|
||||
return;
|
||||
};
|
||||
let Some(start) = snapshot.anchor_in_excerpt(excerpt_id, source_range.start) else {
|
||||
let Some(start) = snapshot.as_singleton_anchor(source_range.start) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -1694,13 +1686,10 @@ mod tests {
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||
let start = snapshot
|
||||
.anchor_in_excerpt(excerpt_id, completion.replace_range.start)
|
||||
let range = snapshot
|
||||
.anchor_range_in_excerpt(excerpt_id, completion.replace_range)
|
||||
.unwrap();
|
||||
let end = snapshot
|
||||
.anchor_in_excerpt(excerpt_id, completion.replace_range.end)
|
||||
.unwrap();
|
||||
editor.edit([(start..end, completion.new_text)], cx);
|
||||
editor.edit([(range, completion.new_text)], cx);
|
||||
(completion.confirm.unwrap())(CompletionIntent::Complete, window, cx);
|
||||
});
|
||||
|
||||
|
||||
@@ -1878,12 +1878,7 @@ impl CodeActionProvider for AssistantCodeActionProvider {
|
||||
}
|
||||
|
||||
let multibuffer_snapshot = multibuffer.read(cx);
|
||||
Some(
|
||||
multibuffer_snapshot
|
||||
.anchor_in_excerpt(excerpt_id, action.range.start)?
|
||||
..multibuffer_snapshot
|
||||
.anchor_in_excerpt(excerpt_id, action.range.end)?,
|
||||
)
|
||||
multibuffer_snapshot.anchor_range_in_excerpt(excerpt_id, action.range)
|
||||
})
|
||||
})?
|
||||
.context("invalid range")?;
|
||||
|
||||
@@ -602,10 +602,8 @@ impl TextThreadEditor {
|
||||
if let Some((crease_id, start)) = self.pending_thought_process.take() {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||
let (excerpt_id, _, _) = multi_buffer_snapshot.as_singleton().unwrap();
|
||||
let start_anchor = multi_buffer_snapshot
|
||||
.anchor_in_excerpt(*excerpt_id, start)
|
||||
.unwrap();
|
||||
let start_anchor =
|
||||
multi_buffer_snapshot.as_singleton_anchor(start).unwrap();
|
||||
|
||||
editor.display_map.update(cx, |display_map, cx| {
|
||||
display_map.unfold_intersecting(
|
||||
@@ -696,13 +694,10 @@ impl TextThreadEditor {
|
||||
}
|
||||
};
|
||||
|
||||
let start = buffer
|
||||
.anchor_in_excerpt(excerpt_id, command.source_range.start)
|
||||
let range = buffer
|
||||
.anchor_range_in_excerpt(excerpt_id, command.source_range.clone())
|
||||
.unwrap();
|
||||
let end = buffer
|
||||
.anchor_in_excerpt(excerpt_id, command.source_range.end)
|
||||
.unwrap();
|
||||
Crease::inline(start..end, placeholder, render_toggle, render_trailer)
|
||||
Crease::inline(range, placeholder, render_toggle, render_trailer)
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
@@ -773,14 +768,11 @@ impl TextThreadEditor {
|
||||
let (&excerpt_id, _buffer_id, _buffer_snapshot) =
|
||||
buffer.as_singleton().unwrap();
|
||||
|
||||
let start = buffer
|
||||
.anchor_in_excerpt(excerpt_id, invoked_slash_command.range.start)
|
||||
.unwrap();
|
||||
let end = buffer
|
||||
.anchor_in_excerpt(excerpt_id, invoked_slash_command.range.end)
|
||||
let range = buffer
|
||||
.anchor_range_in_excerpt(excerpt_id, invoked_slash_command.range.clone())
|
||||
.unwrap();
|
||||
editor.remove_folds_with_type(
|
||||
&[start..end],
|
||||
&[range],
|
||||
TypeId::of::<PendingSlashCommand>(),
|
||||
false,
|
||||
cx,
|
||||
@@ -797,14 +789,11 @@ impl TextThreadEditor {
|
||||
let (&excerpt_id, _buffer_id, _buffer_snapshot) =
|
||||
buffer.as_singleton().unwrap();
|
||||
let context = self.context.downgrade();
|
||||
let crease_start = buffer
|
||||
.anchor_in_excerpt(excerpt_id, invoked_slash_command.range.start)
|
||||
.unwrap();
|
||||
let crease_end = buffer
|
||||
.anchor_in_excerpt(excerpt_id, invoked_slash_command.range.end)
|
||||
let range = buffer
|
||||
.anchor_range_in_excerpt(excerpt_id, invoked_slash_command.range.clone())
|
||||
.unwrap();
|
||||
let crease = Crease::inline(
|
||||
crease_start..crease_end,
|
||||
range,
|
||||
invoked_slash_command_fold_placeholder(command_id, context),
|
||||
fold_toggle("invoked-slash-command"),
|
||||
|_row, _folded, _window, _cx| Empty.into_any(),
|
||||
@@ -842,17 +831,14 @@ impl TextThreadEditor {
|
||||
let mut buffer_rows_to_fold = BTreeSet::new();
|
||||
let mut creases = Vec::new();
|
||||
for (section, status) in sections {
|
||||
let start = buffer
|
||||
.anchor_in_excerpt(excerpt_id, section.range.start)
|
||||
let range = buffer
|
||||
.anchor_range_in_excerpt(excerpt_id, section.range)
|
||||
.unwrap();
|
||||
let end = buffer
|
||||
.anchor_in_excerpt(excerpt_id, section.range.end)
|
||||
.unwrap();
|
||||
let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
|
||||
let buffer_row = MultiBufferRow(range.start.to_point(&buffer).row);
|
||||
buffer_rows_to_fold.insert(buffer_row);
|
||||
creases.push(
|
||||
Crease::inline(
|
||||
start..end,
|
||||
range,
|
||||
FoldPlaceholder {
|
||||
render: render_thought_process_fold_icon_button(
|
||||
cx.entity().downgrade(),
|
||||
@@ -894,17 +880,14 @@ impl TextThreadEditor {
|
||||
let mut buffer_rows_to_fold = BTreeSet::new();
|
||||
let mut creases = Vec::new();
|
||||
for section in sections {
|
||||
let start = buffer
|
||||
.anchor_in_excerpt(excerpt_id, section.range.start)
|
||||
let range = buffer
|
||||
.anchor_range_in_excerpt(excerpt_id, section.range)
|
||||
.unwrap();
|
||||
let end = buffer
|
||||
.anchor_in_excerpt(excerpt_id, section.range.end)
|
||||
.unwrap();
|
||||
let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
|
||||
let buffer_row = MultiBufferRow(range.start.to_point(&buffer).row);
|
||||
buffer_rows_to_fold.insert(buffer_row);
|
||||
creases.push(
|
||||
Crease::inline(
|
||||
start..end,
|
||||
range,
|
||||
FoldPlaceholder {
|
||||
render: render_fold_icon_button(
|
||||
cx.entity().downgrade(),
|
||||
|
||||
@@ -5989,15 +5989,8 @@ impl Editor {
|
||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||
let newest_anchor = self.selections.newest_anchor();
|
||||
let replace_range_multibuffer = {
|
||||
let excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
|
||||
let multibuffer_anchor = snapshot
|
||||
.anchor_in_excerpt(excerpt.id(), buffer.anchor_before(replace_range.start))
|
||||
.unwrap()
|
||||
..snapshot
|
||||
.anchor_in_excerpt(excerpt.id(), buffer.anchor_before(replace_range.end))
|
||||
.unwrap();
|
||||
multibuffer_anchor.start.to_offset(&snapshot)
|
||||
..multibuffer_anchor.end.to_offset(&snapshot)
|
||||
let mut excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
|
||||
excerpt.map_range_from_buffer(replace_range.clone())
|
||||
};
|
||||
if snapshot.buffer_id_for_anchor(newest_anchor.head()) != Some(buffer.remote_id()) {
|
||||
return None;
|
||||
@@ -7981,9 +7974,10 @@ impl Editor {
|
||||
let edits = edits
|
||||
.into_iter()
|
||||
.flat_map(|(range, new_text)| {
|
||||
let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
|
||||
let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
|
||||
Some((start..end, new_text))
|
||||
Some((
|
||||
multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
|
||||
new_text,
|
||||
))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if edits.is_empty() {
|
||||
|
||||
@@ -7464,8 +7464,8 @@ impl EditorElement {
|
||||
}
|
||||
let clipped_start = range.start.max(&buffer_range.start, buffer);
|
||||
let clipped_end = range.end.min(&buffer_range.end, buffer);
|
||||
let range = buffer_snapshot.anchor_in_excerpt(excerpt_id, clipped_start)?
|
||||
..buffer_snapshot.anchor_in_excerpt(excerpt_id, clipped_end)?;
|
||||
let range = buffer_snapshot
|
||||
.anchor_range_in_excerpt(excerpt_id, clipped_start..clipped_end)?;
|
||||
let start = range.start.to_display_point(display_snapshot);
|
||||
let end = range.end.to_display_point(display_snapshot);
|
||||
let selection_layout = SelectionLayout {
|
||||
|
||||
@@ -534,10 +534,9 @@ pub fn show_link_definition(
|
||||
if let Some((url_range, url)) = find_url(&buffer, text_anchor, cx.clone()) {
|
||||
this.read_with(cx, |_, _| {
|
||||
let range = maybe!({
|
||||
let start =
|
||||
snapshot.anchor_in_excerpt(excerpt_id, url_range.start)?;
|
||||
let end = snapshot.anchor_in_excerpt(excerpt_id, url_range.end)?;
|
||||
Some(RangeInEditor::Text(start..end))
|
||||
let range =
|
||||
snapshot.anchor_range_in_excerpt(excerpt_id, url_range)?;
|
||||
Some(RangeInEditor::Text(range))
|
||||
});
|
||||
(range, vec![HoverLink::Url(url)])
|
||||
})
|
||||
@@ -546,10 +545,9 @@ pub fn show_link_definition(
|
||||
find_file(&buffer, project.clone(), text_anchor, cx).await
|
||||
{
|
||||
let range = maybe!({
|
||||
let start =
|
||||
snapshot.anchor_in_excerpt(excerpt_id, filename_range.start)?;
|
||||
let end = snapshot.anchor_in_excerpt(excerpt_id, filename_range.end)?;
|
||||
Some(RangeInEditor::Text(start..end))
|
||||
let range =
|
||||
snapshot.anchor_range_in_excerpt(excerpt_id, filename_range)?;
|
||||
Some(RangeInEditor::Text(range))
|
||||
});
|
||||
|
||||
Some((range, vec![HoverLink::File(filename)]))
|
||||
@@ -562,13 +560,11 @@ pub fn show_link_definition(
|
||||
(
|
||||
definition_result.iter().find_map(|link| {
|
||||
link.origin.as_ref().and_then(|origin| {
|
||||
let start = snapshot.anchor_in_excerpt(
|
||||
let range = snapshot.anchor_range_in_excerpt(
|
||||
excerpt_id,
|
||||
origin.range.start,
|
||||
origin.range.clone(),
|
||||
)?;
|
||||
let end = snapshot
|
||||
.anchor_in_excerpt(excerpt_id, origin.range.end)?;
|
||||
Some(RangeInEditor::Text(start..end))
|
||||
Some(RangeInEditor::Text(range))
|
||||
})
|
||||
}),
|
||||
definition_result.into_iter().map(HoverLink::Text).collect(),
|
||||
|
||||
@@ -467,13 +467,10 @@ fn show_hover(
|
||||
let range = hover_result
|
||||
.range
|
||||
.and_then(|range| {
|
||||
let start = snapshot
|
||||
let range = snapshot
|
||||
.buffer_snapshot()
|
||||
.anchor_in_excerpt(excerpt_id, range.start)?;
|
||||
let end = snapshot
|
||||
.buffer_snapshot()
|
||||
.anchor_in_excerpt(excerpt_id, range.end)?;
|
||||
Some(start..end)
|
||||
.anchor_range_in_excerpt(excerpt_id, range)?;
|
||||
Some(range)
|
||||
})
|
||||
.or_else(|| {
|
||||
let snapshot = &snapshot.buffer_snapshot();
|
||||
|
||||
@@ -364,10 +364,9 @@ impl FollowableItem for Editor {
|
||||
) {
|
||||
let buffer = self.buffer.read(cx);
|
||||
let buffer = buffer.read(cx);
|
||||
let Some((excerpt_id, _, _)) = buffer.as_singleton() else {
|
||||
let Some(position) = buffer.as_singleton_anchor(location) else {
|
||||
return;
|
||||
};
|
||||
let position = buffer.anchor_in_excerpt(*excerpt_id, location).unwrap();
|
||||
let selection = Selection {
|
||||
id: 0,
|
||||
reversed: false,
|
||||
|
||||
@@ -251,25 +251,14 @@ impl Editor {
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let Some(color_start_anchor) = multi_buffer_snapshot
|
||||
.anchor_in_excerpt(
|
||||
*excerpt_id,
|
||||
buffer_snapshot.anchor_before(
|
||||
buffer_snapshot
|
||||
.clip_point_utf16(color_start, Bias::Left),
|
||||
),
|
||||
)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let Some(color_end_anchor) = multi_buffer_snapshot
|
||||
.anchor_in_excerpt(
|
||||
*excerpt_id,
|
||||
buffer_snapshot.anchor_after(
|
||||
buffer_snapshot
|
||||
.clip_point_utf16(color_end, Bias::Right),
|
||||
),
|
||||
)
|
||||
let start = buffer_snapshot.anchor_before(
|
||||
buffer_snapshot.clip_point_utf16(color_start, Bias::Left),
|
||||
);
|
||||
let end = buffer_snapshot.anchor_after(
|
||||
buffer_snapshot.clip_point_utf16(color_end, Bias::Right),
|
||||
);
|
||||
let Some(range) = multi_buffer_snapshot
|
||||
.anchor_range_in_excerpt(*excerpt_id, start..end)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
@@ -285,16 +274,14 @@ impl Editor {
|
||||
new_buffer_colors.binary_search_by(|(probe, _)| {
|
||||
probe
|
||||
.start
|
||||
.cmp(&color_start_anchor, &multi_buffer_snapshot)
|
||||
.cmp(&range.start, &multi_buffer_snapshot)
|
||||
.then_with(|| {
|
||||
probe.end.cmp(
|
||||
&color_end_anchor,
|
||||
&multi_buffer_snapshot,
|
||||
)
|
||||
probe
|
||||
.end
|
||||
.cmp(&range.end, &multi_buffer_snapshot)
|
||||
})
|
||||
});
|
||||
new_buffer_colors
|
||||
.insert(i, (color_start_anchor..color_end_anchor, color));
|
||||
new_buffer_colors.insert(i, (range, color));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,11 +234,7 @@ fn conflicts_updated(
|
||||
continue;
|
||||
};
|
||||
let excerpt_id = *excerpt_id;
|
||||
let Some(range) = snapshot
|
||||
.anchor_in_excerpt(excerpt_id, conflict_range.start)
|
||||
.zip(snapshot.anchor_in_excerpt(excerpt_id, conflict_range.end))
|
||||
.map(|(start, end)| start..end)
|
||||
else {
|
||||
let Some(range) = snapshot.anchor_range_in_excerpt(excerpt_id, conflict_range) else {
|
||||
continue;
|
||||
};
|
||||
removed_highlighted_ranges.push(range.clone());
|
||||
@@ -321,27 +317,12 @@ fn update_conflict_highlighting(
|
||||
buffer: &editor::MultiBufferSnapshot,
|
||||
excerpt_id: editor::ExcerptId,
|
||||
cx: &mut Context<Editor>,
|
||||
) {
|
||||
) -> Option<()> {
|
||||
log::debug!("update conflict highlighting for {conflict:?}");
|
||||
|
||||
let outer_start = buffer
|
||||
.anchor_in_excerpt(excerpt_id, conflict.range.start)
|
||||
.unwrap();
|
||||
let outer_end = buffer
|
||||
.anchor_in_excerpt(excerpt_id, conflict.range.end)
|
||||
.unwrap();
|
||||
let our_start = buffer
|
||||
.anchor_in_excerpt(excerpt_id, conflict.ours.start)
|
||||
.unwrap();
|
||||
let our_end = buffer
|
||||
.anchor_in_excerpt(excerpt_id, conflict.ours.end)
|
||||
.unwrap();
|
||||
let their_start = buffer
|
||||
.anchor_in_excerpt(excerpt_id, conflict.theirs.start)
|
||||
.unwrap();
|
||||
let their_end = buffer
|
||||
.anchor_in_excerpt(excerpt_id, conflict.theirs.end)
|
||||
.unwrap();
|
||||
let outer = buffer.anchor_range_in_excerpt(excerpt_id, conflict.range.clone())?;
|
||||
let ours = buffer.anchor_range_in_excerpt(excerpt_id, conflict.ours.clone())?;
|
||||
let theirs = buffer.anchor_range_in_excerpt(excerpt_id, conflict.theirs.clone())?;
|
||||
|
||||
let ours_background = cx.theme().colors().version_control_conflict_marker_ours;
|
||||
let theirs_background = cx.theme().colors().version_control_conflict_marker_theirs;
|
||||
@@ -352,32 +333,29 @@ fn update_conflict_highlighting(
|
||||
};
|
||||
|
||||
editor.insert_gutter_highlight::<ConflictsOuter>(
|
||||
outer_start..their_end,
|
||||
outer.start..theirs.end,
|
||||
|cx| cx.theme().colors().editor_background,
|
||||
cx,
|
||||
);
|
||||
|
||||
// Prevent diff hunk highlighting within the entire conflict region.
|
||||
editor.highlight_rows::<ConflictsOuter>(outer_start..outer_end, theirs_background, options, cx);
|
||||
editor.highlight_rows::<ConflictsOurs>(our_start..our_end, ours_background, options, cx);
|
||||
editor.highlight_rows::<ConflictsOuter>(outer.clone(), theirs_background, options, cx);
|
||||
editor.highlight_rows::<ConflictsOurs>(ours.clone(), ours_background, options, cx);
|
||||
editor.highlight_rows::<ConflictsOursMarker>(
|
||||
outer_start..our_start,
|
||||
outer.start..ours.start,
|
||||
ours_background,
|
||||
options,
|
||||
cx,
|
||||
);
|
||||
editor.highlight_rows::<ConflictsTheirs>(
|
||||
their_start..their_end,
|
||||
theirs_background,
|
||||
options,
|
||||
cx,
|
||||
);
|
||||
editor.highlight_rows::<ConflictsTheirs>(theirs.clone(), theirs_background, options, cx);
|
||||
editor.highlight_rows::<ConflictsTheirsMarker>(
|
||||
their_end..outer_end,
|
||||
theirs.end..outer.end,
|
||||
theirs_background,
|
||||
options,
|
||||
cx,
|
||||
);
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
fn render_conflict_buttons(
|
||||
@@ -488,20 +466,16 @@ pub(crate) fn resolve_conflict(
|
||||
})
|
||||
.ok()?;
|
||||
let &(_, block_id) = &state.block_ids[ix];
|
||||
let start = snapshot
|
||||
.anchor_in_excerpt(excerpt_id, resolved_conflict.range.start)
|
||||
.unwrap();
|
||||
let end = snapshot
|
||||
.anchor_in_excerpt(excerpt_id, resolved_conflict.range.end)
|
||||
.unwrap();
|
||||
let range =
|
||||
snapshot.anchor_range_in_excerpt(excerpt_id, resolved_conflict.range)?;
|
||||
|
||||
editor.remove_gutter_highlights::<ConflictsOuter>(vec![start..end], cx);
|
||||
editor.remove_gutter_highlights::<ConflictsOuter>(vec![range.clone()], cx);
|
||||
|
||||
editor.remove_highlighted_rows::<ConflictsOuter>(vec![start..end], cx);
|
||||
editor.remove_highlighted_rows::<ConflictsOurs>(vec![start..end], cx);
|
||||
editor.remove_highlighted_rows::<ConflictsTheirs>(vec![start..end], cx);
|
||||
editor.remove_highlighted_rows::<ConflictsOursMarker>(vec![start..end], cx);
|
||||
editor.remove_highlighted_rows::<ConflictsTheirsMarker>(vec![start..end], cx);
|
||||
editor.remove_highlighted_rows::<ConflictsOuter>(vec![range.clone()], cx);
|
||||
editor.remove_highlighted_rows::<ConflictsOurs>(vec![range.clone()], cx);
|
||||
editor.remove_highlighted_rows::<ConflictsTheirs>(vec![range.clone()], cx);
|
||||
editor.remove_highlighted_rows::<ConflictsOursMarker>(vec![range.clone()], cx);
|
||||
editor.remove_highlighted_rows::<ConflictsTheirsMarker>(vec![range], cx);
|
||||
editor.remove_blocks(HashSet::from_iter([block_id]), None, cx);
|
||||
Some((workspace, project, multibuffer, buffer))
|
||||
})
|
||||
|
||||
@@ -356,12 +356,7 @@ impl SyntaxTreeView {
|
||||
let multibuffer = editor_state.editor.read(cx).buffer();
|
||||
let multibuffer = multibuffer.read(cx).snapshot(cx);
|
||||
let excerpt_id = buffer_state.excerpt_id;
|
||||
let range = multibuffer
|
||||
.anchor_in_excerpt(excerpt_id, range.start)
|
||||
.unwrap()
|
||||
..multibuffer
|
||||
.anchor_in_excerpt(excerpt_id, range.end)
|
||||
.unwrap();
|
||||
let range = multibuffer.anchor_range_in_excerpt(excerpt_id, range)?;
|
||||
|
||||
// Update the editor with the anchor range.
|
||||
editor_state.editor.update(cx, |editor, cx| {
|
||||
|
||||
@@ -5230,8 +5230,30 @@ impl MultiBufferSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps the [`text::Anchor`] in a [`multi_buffer::Anchor`] if this multi-buffer is a singleton.
|
||||
pub fn as_singleton_anchor(&self, text_anchor: text::Anchor) -> Option<Anchor> {
|
||||
let (excerpt, buffer, _) = self.as_singleton()?;
|
||||
Some(Anchor::in_buffer(*excerpt, buffer, text_anchor))
|
||||
}
|
||||
|
||||
/// Returns an anchor for the given excerpt and text anchor,
|
||||
/// returns None if the excerpt_id is no longer valid.
|
||||
/// Returns [`None`] if the excerpt_id is no longer valid or the text anchor range is out of excerpt's bounds.
|
||||
pub fn anchor_range_in_excerpt(
|
||||
&self,
|
||||
excerpt_id: ExcerptId,
|
||||
text_anchor: Range<text::Anchor>,
|
||||
) -> Option<Range<Anchor>> {
|
||||
let excerpt_id = self.latest_excerpt_id(excerpt_id);
|
||||
let excerpt = self.excerpt(excerpt_id)?;
|
||||
|
||||
Some(
|
||||
self.anchor_in_excerpt_(excerpt, text_anchor.start)?
|
||||
..self.anchor_in_excerpt_(excerpt, text_anchor.end)?,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns an anchor for the given excerpt and text anchor,
|
||||
/// Returns [`None`] if the excerpt_id is no longer valid or the text anchor range is out of excerpt's bounds.
|
||||
pub fn anchor_in_excerpt(
|
||||
&self,
|
||||
excerpt_id: ExcerptId,
|
||||
@@ -5239,8 +5261,32 @@ impl MultiBufferSnapshot {
|
||||
) -> Option<Anchor> {
|
||||
let excerpt_id = self.latest_excerpt_id(excerpt_id);
|
||||
let excerpt = self.excerpt(excerpt_id)?;
|
||||
self.anchor_in_excerpt_(excerpt, text_anchor)
|
||||
}
|
||||
|
||||
fn anchor_in_excerpt_(&self, excerpt: &Excerpt, text_anchor: text::Anchor) -> Option<Anchor> {
|
||||
match text_anchor.buffer_id {
|
||||
Some(buffer_id) if buffer_id == excerpt.buffer_id => (),
|
||||
Some(_) => return None,
|
||||
None if text_anchor == text::Anchor::MAX || text_anchor == text::Anchor::MIN => {
|
||||
return Some(Anchor::in_buffer(
|
||||
excerpt.id,
|
||||
excerpt.buffer_id,
|
||||
text_anchor,
|
||||
));
|
||||
}
|
||||
None => return None,
|
||||
}
|
||||
|
||||
let context = &excerpt.range.context;
|
||||
if context.start.cmp(&text_anchor, &excerpt.buffer).is_gt()
|
||||
|| context.end.cmp(&text_anchor, &excerpt.buffer).is_lt()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Anchor::in_buffer(
|
||||
excerpt_id,
|
||||
excerpt.id,
|
||||
excerpt.buffer_id,
|
||||
text_anchor,
|
||||
))
|
||||
@@ -6075,22 +6121,15 @@ impl MultiBufferSnapshot {
|
||||
.flat_map(|item| {
|
||||
Some(OutlineItem {
|
||||
depth: item.depth,
|
||||
range: self.anchor_in_excerpt(*excerpt_id, item.range.start)?
|
||||
..self.anchor_in_excerpt(*excerpt_id, item.range.end)?,
|
||||
range: self.anchor_range_in_excerpt(*excerpt_id, item.range)?,
|
||||
text: item.text,
|
||||
highlight_ranges: item.highlight_ranges,
|
||||
name_ranges: item.name_ranges,
|
||||
body_range: item.body_range.and_then(|body_range| {
|
||||
Some(
|
||||
self.anchor_in_excerpt(*excerpt_id, body_range.start)?
|
||||
..self.anchor_in_excerpt(*excerpt_id, body_range.end)?,
|
||||
)
|
||||
self.anchor_range_in_excerpt(*excerpt_id, body_range)
|
||||
}),
|
||||
annotation_range: item.annotation_range.and_then(|annotation_range| {
|
||||
Some(
|
||||
self.anchor_in_excerpt(*excerpt_id, annotation_range.start)?
|
||||
..self.anchor_in_excerpt(*excerpt_id, annotation_range.end)?,
|
||||
)
|
||||
self.anchor_range_in_excerpt(*excerpt_id, annotation_range)
|
||||
}),
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3192,13 +3192,13 @@ impl OutlinePanel {
|
||||
.into_iter()
|
||||
.flat_map(|excerpt| excerpt.iter_outlines())
|
||||
.flat_map(|outline| {
|
||||
let start = multi_buffer_snapshot
|
||||
.anchor_in_excerpt(excerpt_id, outline.range.start)?
|
||||
.to_display_point(&editor_snapshot);
|
||||
let end = multi_buffer_snapshot
|
||||
.anchor_in_excerpt(excerpt_id, outline.range.end)?
|
||||
.to_display_point(&editor_snapshot);
|
||||
Some((start..end, outline))
|
||||
let range = multi_buffer_snapshot
|
||||
.anchor_range_in_excerpt(excerpt_id, outline.range.clone())?;
|
||||
Some((
|
||||
range.start.to_display_point(&editor_snapshot)
|
||||
..range.end.to_display_point(&editor_snapshot),
|
||||
outline,
|
||||
))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user