Compare commits

...

1 Commits

Author SHA1 Message Date
Michael Sloan
2a55062dd1 Refactor multibuffer methods for multibuffer range -> buffer range
* Renames `excerpts_for_ranges` to `disjoint_ranges_to_buffer_ranges`,
and modifies it to return `MultiBufferExcerpt` since it provides more
information and was already being built.

* `range_to_buffer_ranges` now returns an iterator, should improve
efficiency in a few places.  Now implemented by just calling
`disjoint_ranges_to_buffer_ranges`.
2025-01-06 19:05:57 -07:00
9 changed files with 123 additions and 137 deletions

View File

@@ -228,19 +228,20 @@ impl InlineAssistant {
let newest_selection = newest_selection.unwrap();
let mut codegen_ranges = Vec::new();
for (excerpt_id, buffer, buffer_range) in
snapshot.excerpts_in_ranges(selections.iter().map(|selection| {
for (excerpt, buffer_range) in
snapshot.disjoint_ranges_to_buffer_ranges(selections.iter().map(|selection| {
snapshot.anchor_before(selection.start)..snapshot.anchor_after(selection.end)
}))
{
let buffer = excerpt.buffer();
let start = Anchor {
buffer_id: Some(buffer.remote_id()),
excerpt_id,
excerpt_id: excerpt.id(),
text_anchor: buffer.anchor_before(buffer_range.start),
};
let end = Anchor {
buffer_id: Some(buffer.remote_id()),
excerpt_id,
excerpt_id: excerpt.id(),
text_anchor: buffer.anchor_after(buffer_range.end),
};
codegen_ranges.push(start..end);
@@ -798,9 +799,10 @@ impl InlineAssistant {
let language_name = assist.editor.upgrade().and_then(|editor| {
let multibuffer = editor.read(cx).buffer().read(cx);
let multibuffer_snapshot = multibuffer.snapshot(cx);
let ranges = multibuffer_snapshot.range_to_buffer_ranges(assist.range.clone());
let mut ranges =
multibuffer_snapshot.range_to_buffer_ranges(assist.range.clone());
ranges
.first()
.next()
.and_then(|(excerpt, _)| excerpt.buffer().language())
.map(|language| language.name())
});
@@ -2625,9 +2627,10 @@ impl CodegenAlternative {
) -> Self {
let snapshot = multi_buffer.read(cx).snapshot(cx);
// TODO: Could be made more efficient by using a reverse iterator.
let (old_excerpt, _) = snapshot
.range_to_buffer_ranges(range.clone())
.pop()
.last()
.unwrap();
let old_buffer = cx.new_model(|cx| {
let text = old_excerpt.buffer().as_rope().clone();
@@ -2872,9 +2875,9 @@ impl CodegenAlternative {
let language_name = {
let multibuffer = self.buffer.read(cx);
let snapshot = multibuffer.snapshot(cx);
let ranges = snapshot.range_to_buffer_ranges(self.range.clone());
let mut ranges = snapshot.range_to_buffer_ranges(self.range.clone());
ranges
.first()
.next()
.and_then(|(excerpt, _)| excerpt.buffer().language())
.map(|language| language.name())
};

View File

@@ -257,9 +257,10 @@ impl CodegenAlternative {
) -> Self {
let snapshot = buffer.read(cx).snapshot(cx);
// TODO: Could be more efficient by using a reverse iterator.
let (old_excerpt, _) = snapshot
.range_to_buffer_ranges(range.clone())
.pop()
.last()
.unwrap();
let old_buffer = cx.new_model(|cx| {
let text = old_excerpt.buffer().as_rope().clone();
@@ -475,9 +476,9 @@ impl CodegenAlternative {
let language_name = {
let multibuffer = self.buffer.read(cx);
let snapshot = multibuffer.snapshot(cx);
let ranges = snapshot.range_to_buffer_ranges(self.range.clone());
let mut ranges = snapshot.range_to_buffer_ranges(self.range.clone());
ranges
.first()
.next()
.and_then(|(excerpt, _)| excerpt.buffer().language())
.map(|language| language.name())
};

View File

@@ -294,19 +294,20 @@ impl InlineAssistant {
let newest_selection = newest_selection.unwrap();
let mut codegen_ranges = Vec::new();
for (excerpt_id, buffer, buffer_range) in
snapshot.excerpts_in_ranges(selections.iter().map(|selection| {
for (excerpt, buffer_range) in
snapshot.disjoint_ranges_to_buffer_ranges(selections.iter().map(|selection| {
snapshot.anchor_before(selection.start)..snapshot.anchor_after(selection.end)
}))
{
let buffer = excerpt.buffer();
let start = Anchor {
buffer_id: Some(buffer.remote_id()),
excerpt_id,
excerpt_id: excerpt.id(),
text_anchor: buffer.anchor_before(buffer_range.start),
};
let end = Anchor {
buffer_id: Some(buffer.remote_id()),
excerpt_id,
excerpt_id: excerpt.id(),
text_anchor: buffer.anchor_after(buffer_range.end),
};
codegen_ranges.push(start..end);
@@ -872,9 +873,9 @@ impl InlineAssistant {
let language_name = assist.editor.upgrade().and_then(|editor| {
let multibuffer = editor.read(cx).buffer().read(cx);
let snapshot = multibuffer.snapshot(cx);
let ranges = snapshot.range_to_buffer_ranges(assist.range.clone());
let mut ranges = snapshot.range_to_buffer_ranges(assist.range.clone());
ranges
.first()
.next()
.and_then(|(excerpt, _)| excerpt.buffer().language())
.map(|language| language.name())
});

View File

@@ -109,7 +109,7 @@ pub use proposed_changes_editor::{
ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
};
use similar::{ChangeTag, TextDiff};
use std::iter::Peekable;
use std::iter::{self, Peekable};
use task::{ResolvedTask, TaskTemplate, TaskVariables};
use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
@@ -3550,8 +3550,7 @@ impl Editor {
);
let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
multi_buffer_snapshot
.range_to_buffer_ranges(multi_buffer_visible_range)
.into_iter()
.disjoint_ranges_to_buffer_ranges(iter::once(multi_buffer_visible_range))
.filter(|(_, excerpt_visible_range)| !excerpt_visible_range.is_empty())
.filter_map(|(excerpt, excerpt_visible_range)| {
let buffer_file = project::File::from_dyn(excerpt.buffer().file())?;
@@ -10498,13 +10497,13 @@ impl Editor {
} else {
let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
let mut toggled_buffers = HashSet::default();
for (_, buffer_snapshot, _) in multi_buffer_snapshot.excerpts_in_ranges(
for (excerpt, _) in multi_buffer_snapshot.disjoint_ranges_to_buffer_ranges(
self.selections
.disjoint_anchors()
.into_iter()
.map(|selection| selection.range()),
) {
let buffer_id = buffer_snapshot.remote_id();
let buffer_id = excerpt.buffer().remote_id();
if toggled_buffers.insert(buffer_id) {
if self.buffer_folded(buffer_id, cx) {
self.unfold_buffer(buffer_id, cx);
@@ -10584,13 +10583,13 @@ impl Editor {
} else {
let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
let mut folded_buffers = HashSet::default();
for (_, buffer_snapshot, _) in multi_buffer_snapshot.excerpts_in_ranges(
for (excerpt, _) in multi_buffer_snapshot.disjoint_ranges_to_buffer_ranges(
self.selections
.disjoint_anchors()
.into_iter()
.map(|selection| selection.range()),
) {
let buffer_id = buffer_snapshot.remote_id();
let buffer_id = excerpt.buffer().remote_id();
if folded_buffers.insert(buffer_id) {
self.fold_buffer(buffer_id, cx);
}
@@ -10750,13 +10749,13 @@ impl Editor {
} else {
let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
let mut unfolded_buffers = HashSet::default();
for (_, buffer_snapshot, _) in multi_buffer_snapshot.excerpts_in_ranges(
for (excerpt, _) in multi_buffer_snapshot.disjoint_ranges_to_buffer_ranges(
self.selections
.disjoint_anchors()
.into_iter()
.map(|selection| selection.range()),
) {
let buffer_id = buffer_snapshot.remote_id();
let buffer_id = excerpt.buffer().remote_id();
if unfolded_buffers.insert(buffer_id) {
self.unfold_buffer(buffer_id, cx);
}
@@ -11500,36 +11499,36 @@ impl Editor {
}
fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<url::Url>> {
let buffer_and_selection = maybe!({
let selection = self.selections.newest::<Point>(cx);
let buffer_and_selection_rows = maybe!({
let multi_buffer = self.buffer().read(cx);
let multi_buffer_snapshot = multi_buffer.snapshot(cx);
let selection = self.selections.newest_anchor();
let selection_range = selection.range();
let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
(buffer, selection_range.start.row..selection_range.end.row)
let (buffer, selection_rows) = if let Some(buffer) = multi_buffer.as_singleton() {
(
buffer,
selection_range.start.to_point(&multi_buffer_snapshot).row
..selection_range.end.to_point(&multi_buffer_snapshot).row,
)
} else {
let multi_buffer = self.buffer().read(cx);
let multi_buffer_snapshot = multi_buffer.snapshot(cx);
let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
let (excerpt, range) = if selection.reversed {
buffer_ranges.first()
} else {
buffer_ranges.last()
}?;
let selection_head = selection.head();
let excerpt =
multi_buffer_snapshot.excerpt_containing(selection_head..selection_head)?;
let range =
excerpt.map_range_to_buffer(selection_range.to_offset(&multi_buffer_snapshot));
let snapshot = excerpt.buffer();
let selection = text::ToPoint::to_point(&range.start, &snapshot).row
..text::ToPoint::to_point(&range.end, &snapshot).row;
(
multi_buffer.buffer(excerpt.buffer_id()).unwrap().clone(),
selection,
text::ToPoint::to_point(&range.start, &snapshot).row
..text::ToPoint::to_point(&range.end, &snapshot).row,
)
};
Some((buffer, selection))
Some((buffer, selection_rows))
});
let Some((buffer, selection)) = buffer_and_selection else {
let Some((buffer, selection_rows)) = buffer_and_selection_rows else {
return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
};
@@ -11538,7 +11537,7 @@ impl Editor {
};
project.update(cx, |project, cx| {
project.get_permalink_to_line(&buffer, selection, cx)
project.get_permalink_to_line(&buffer, selection_rows, cx)
})
}

View File

@@ -458,10 +458,7 @@ impl Editor {
) -> Option<()> {
let multi_buffer = self.buffer.read(cx);
let multi_buffer_snapshot = multi_buffer.snapshot(cx);
let (excerpt, range) = multi_buffer_snapshot
.range_to_buffer_ranges(range)
.into_iter()
.next()?;
let (excerpt, range) = multi_buffer_snapshot.range_to_buffer_ranges(range).next()?;
multi_buffer
.buffer(excerpt.buffer_id())

View File

@@ -1468,10 +1468,11 @@ impl SearchableItem for Editor {
search_within_ranges
};
for (excerpt_id, search_buffer, search_range) in
buffer.excerpts_in_ranges(search_within_ranges)
for (excerpt, search_range) in
buffer.disjoint_ranges_to_buffer_ranges(search_within_ranges)
{
if !search_range.is_empty() {
let search_buffer = excerpt.buffer();
ranges.extend(
query
.search(search_buffer, Some(search_range.clone()))
@@ -1482,8 +1483,8 @@ impl SearchableItem for Editor {
.anchor_after(search_range.start + match_range.start);
let end = search_buffer
.anchor_before(search_range.start + match_range.end);
buffer.anchor_in_excerpt(excerpt_id, start).unwrap()
..buffer.anchor_in_excerpt(excerpt_id, end).unwrap()
buffer.anchor_in_excerpt(excerpt.id(), start).unwrap()
..buffer.anchor_in_excerpt(excerpt.id(), end).unwrap()
}),
);
}

View File

@@ -132,12 +132,14 @@ impl SyntaxTreeView {
.editor
.update(cx, |editor, cx| editor.snapshot(cx));
let (excerpt, buffer, range) = editor_state.editor.update(cx, |editor, cx| {
let selection = editor.selections.last::<usize>(cx);
let selection_range = editor.selections.last::<usize>(cx).range();
let selection_head = selection.head();
let multi_buffer = editor.buffer().read(cx);
let (excerpt, range) = snapshot
let excerpt = snapshot
.buffer_snapshot
.range_to_buffer_ranges(selection_range)
.pop()?;
.excerpt_containing(selection_head..selection_head)?;
let range = excerpt.map_range_to_buffer(selection_range);
let buffer = multi_buffer.buffer(excerpt.buffer_id()).unwrap().clone();
Some((excerpt, buffer, range))
})?;

View File

@@ -3894,7 +3894,7 @@ impl MultiBufferSnapshot {
where
T: 'a + ToOffset,
{
let mut ranges = self.range_to_buffer_ranges(range);
let mut ranges = self.range_to_buffer_ranges(range).collect::<Vec<_>>();
if reversed {
ranges.reverse();
}
@@ -4157,80 +4157,60 @@ impl MultiBufferSnapshot {
})
}
pub fn range_to_buffer_ranges<T: ToOffset>(
&self,
range: Range<T>,
) -> Vec<(MultiBufferExcerpt<'_>, Range<usize>)> {
let start = range.start.to_offset(self);
let end = range.end.to_offset(self);
let mut result = Vec::new();
let mut cursor = self.excerpts.cursor::<(usize, Point)>(&());
cursor.seek(&start, Bias::Right, &());
if cursor.item().is_none() {
cursor.prev(&());
}
while let Some(excerpt) = cursor.item() {
if cursor.start().0 > end {
break;
}
let mut end_before_newline = cursor.end(&()).0;
if excerpt.has_trailing_newline {
end_before_newline -= 1;
}
let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
let start = excerpt_start + (cmp::max(start, cursor.start().0) - cursor.start().0);
let end = excerpt_start + (cmp::min(end, end_before_newline) - cursor.start().0);
result.push((
MultiBufferExcerpt::new(&excerpt, *cursor.start()),
start..end,
));
cursor.next(&());
}
result
}
/// Returns excerpts overlapping the given ranges. If range spans multiple excerpts returns one range for each excerpt
/// Returns excerpts overlapping the given range. If range spans multiple excerpts returns one
/// range for each excerpt.
///
/// The ranges are specified in the coordinate space of the multibuffer, not the individual excerpted buffers.
/// Each returned excerpt's range is in the coordinate space of its source buffer.
pub fn excerpts_in_ranges(
pub fn range_to_buffer_ranges<T: ToOffset>(
&self,
ranges: impl IntoIterator<Item = Range<Anchor>>,
) -> impl Iterator<Item = (ExcerptId, &BufferSnapshot, Range<usize>)> {
let mut ranges = ranges.into_iter().map(|range| range.to_offset(self));
range: Range<T>,
) -> impl Iterator<Item = (MultiBufferExcerpt<'_>, Range<usize>)> {
self.disjoint_ranges_to_buffer_ranges(iter::once(range))
}
/// Returns excerpts overlapping the given ranges, which must be disjoint and sorted by start position.
/// If range spans multiple excerpts returns one range for each excerpt
///
/// The ranges are specified in the coordinate space of the multibuffer, not the individual excerpted buffers.
/// Each returned excerpt's range is in the coordinate space of its source buffer.
pub fn disjoint_ranges_to_buffer_ranges<T: ToOffset>(
&self,
ranges: impl IntoIterator<Item = Range<T>>,
) -> impl Iterator<Item = (MultiBufferExcerpt<'_>, Range<usize>)> {
let mut ranges = ranges.into_iter();
let mut cursor = self.excerpts.cursor::<(usize, Point)>(&());
cursor.next(&());
let mut current_range = ranges.next();
iter::from_fn(move || {
let range = current_range.clone()?;
if range.start >= cursor.end(&()).0 {
cursor.seek_forward(&range.start, Bias::Right, &());
if range.start == self.len() {
let range = current_range.as_ref()?;
let start = range.start.to_offset(self);
let end = range.end.to_offset(self);
if start >= cursor.end(&()).0 {
cursor.seek_forward(&start, Bias::Right, &());
if start == self.len() {
cursor.prev(&());
}
}
let excerpt = cursor.item()?;
let range_start_in_excerpt = cmp::max(range.start, cursor.start().0);
let range_start_in_excerpt = cmp::max(start, cursor.start().0);
let range_end_in_excerpt = if excerpt.has_trailing_newline {
cmp::min(range.end, cursor.end(&()).0 - 1)
cmp::min(end, cursor.end(&()).0 - 1)
} else {
cmp::min(range.end, cursor.end(&()).0)
cmp::min(end, cursor.end(&()).0)
};
let buffer_range = MultiBufferExcerpt::new(excerpt, *cursor.start())
let multi_buffer_excerpt = MultiBufferExcerpt::new(excerpt, *cursor.start());
let buffer_range = multi_buffer_excerpt
.map_range_to_buffer(range_start_in_excerpt..range_end_in_excerpt);
if range.end > cursor.end(&()).0 {
if end > cursor.end(&()).0 {
cursor.next(&());
} else {
current_range = ranges.next();
}
Some((excerpt.id, &excerpt.buffer, buffer_range))
Some((multi_buffer_excerpt, buffer_range))
})
}

View File

@@ -1235,7 +1235,9 @@ fn test_random_multibuffer(cx: &mut AppContext, mut rng: StdRng) {
);
let snapshot = multibuffer.read(cx).snapshot(cx);
let excerpted_buffer_ranges = snapshot.range_to_buffer_ranges(start_ix..end_ix);
let excerpted_buffer_ranges = snapshot
.range_to_buffer_ranges(start_ix..end_ix)
.collect::<Vec<_>>();
let excerpted_buffers_text = excerpted_buffer_ranges
.iter()
.map(|(excerpt, buffer_range)| {
@@ -1488,7 +1490,7 @@ fn test_excerpts_in_ranges_no_ranges(cx: &mut AppContext) {
let snapshot = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
let mut excerpts = snapshot.excerpts_in_ranges(iter::from_fn(|| None));
let mut excerpts = snapshot.disjoint_ranges_to_buffer_ranges::<usize>(iter::from_fn(|| None));
assert!(excerpts.next().is_none());
}
@@ -1587,12 +1589,12 @@ fn test_excerpts_in_ranges_range_inside_the_excerpt(cx: &mut AppContext) {
)];
let excerpts = snapshot
.excerpts_in_ranges(vec![range.clone()].into_iter())
.map(|(excerpt_id, buffer, actual_range)| {
.disjoint_ranges_to_buffer_ranges(vec![range.clone()].into_iter())
.map(|(excerpt, actual_range)| {
(
excerpt_id,
buffer.remote_id(),
map_range_from_excerpt(&snapshot, excerpt_id, buffer, actual_range),
excerpt.id(),
excerpt.buffer().remote_id(),
map_range_from_excerpt(&snapshot, excerpt.id(), excerpt.buffer(), actual_range),
)
})
.collect_vec();
@@ -1652,12 +1654,12 @@ fn test_excerpts_in_ranges_range_crosses_excerpts_boundary(cx: &mut AppContext)
];
let excerpts = snapshot
.excerpts_in_ranges(vec![expected_range.clone()].into_iter())
.map(|(excerpt_id, buffer, actual_range)| {
.disjoint_ranges_to_buffer_ranges(vec![expected_range.clone()].into_iter())
.map(|(excerpt, actual_range)| {
(
excerpt_id,
buffer.remote_id(),
map_range_from_excerpt(&snapshot, excerpt_id, buffer, actual_range),
excerpt.id(),
excerpt.buffer().remote_id(),
map_range_from_excerpt(&snapshot, excerpt.id(), excerpt.buffer(), actual_range),
)
})
.collect_vec();
@@ -1728,12 +1730,12 @@ fn test_excerpts_in_ranges_range_encloses_excerpt(cx: &mut AppContext) {
];
let excerpts = snapshot
.excerpts_in_ranges(vec![expected_range.clone()].into_iter())
.map(|(excerpt_id, buffer, actual_range)| {
.disjoint_ranges_to_buffer_ranges(vec![expected_range.clone()].into_iter())
.map(|(excerpt, actual_range)| {
(
excerpt_id,
buffer.remote_id(),
map_range_from_excerpt(&snapshot, excerpt_id, buffer, actual_range),
excerpt.id(),
excerpt.buffer().remote_id(),
map_range_from_excerpt(&snapshot, excerpt.id(), excerpt.buffer(), actual_range),
)
})
.collect_vec();
@@ -1794,12 +1796,12 @@ fn test_excerpts_in_ranges_multiple_ranges(cx: &mut AppContext) {
});
let excerpts = snapshot
.excerpts_in_ranges(ranges)
.map(|(excerpt_id, buffer, actual_range)| {
.disjoint_ranges_to_buffer_ranges(ranges)
.map(|(excerpt, actual_range)| {
(
excerpt_id,
buffer.remote_id(),
map_range_from_excerpt(&snapshot, excerpt_id, buffer, actual_range),
excerpt.id(),
excerpt.buffer().remote_id(),
map_range_from_excerpt(&snapshot, excerpt.id(), excerpt.buffer(), actual_range),
)
})
.collect_vec();
@@ -1860,12 +1862,12 @@ fn test_excerpts_in_ranges_range_ends_at_excerpt_end(cx: &mut AppContext) {
];
let excerpts = snapshot
.excerpts_in_ranges(ranges.into_iter())
.map(|(excerpt_id, buffer, actual_range)| {
.disjoint_ranges_to_buffer_ranges(ranges.into_iter())
.map(|(excerpt, actual_range)| {
(
excerpt_id,
buffer.remote_id(),
map_range_from_excerpt(&snapshot, excerpt_id, buffer, actual_range),
excerpt.id(),
excerpt.buffer().remote_id(),
map_range_from_excerpt(&snapshot, excerpt.id(), excerpt.buffer(), actual_range),
)
})
.collect_vec();