Fix regression: Restore creation of multiple assist editors on ctrl-enter when selections span across multiple excerpts (#16190)

Release Notes:

- N/A
This commit is contained in:
Nathan Sobo 2024-08-13 17:19:10 -06:00 committed by GitHub
parent 47628515e1
commit 5cb4de4ec6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 196 additions and 13 deletions

View file

@ -3873,7 +3873,60 @@ impl MultiBufferSnapshot {
}
}
// Takes an iterator over anchor ranges and returns a new iterator over anchor ranges that don't
// span across excerpt boundaries.
pub fn split_ranges<'a, I>(&'a self, ranges: I) -> impl Iterator<Item = Range<Anchor>> + 'a
where
I: IntoIterator<Item = Range<Anchor>> + 'a,
{
let mut ranges = ranges.into_iter().map(|range| range.to_offset(self));
let mut cursor = self.excerpts.cursor::<usize>();
cursor.next(&());
let mut current_range = ranges.next();
iter::from_fn(move || {
let range = current_range.clone()?;
if range.start >= cursor.end(&()) {
cursor.seek_forward(&range.start, Bias::Right, &());
if range.start == self.len() {
cursor.prev(&());
}
}
let excerpt = cursor.item()?;
let range_start_in_excerpt = cmp::max(range.start, *cursor.start());
let range_end_in_excerpt = if excerpt.has_trailing_newline {
cmp::min(range.end, cursor.end(&()) - 1)
} else {
cmp::min(range.end, cursor.end(&()))
};
let buffer_range = MultiBufferExcerpt::new(excerpt, *cursor.start())
.map_range_to_buffer(range_start_in_excerpt..range_end_in_excerpt);
let subrange_start_anchor = Anchor {
buffer_id: Some(excerpt.buffer_id),
excerpt_id: excerpt.id,
text_anchor: excerpt.buffer.anchor_before(buffer_range.start),
};
let subrange_end_anchor = Anchor {
buffer_id: Some(excerpt.buffer_id),
excerpt_id: excerpt.id,
text_anchor: excerpt.buffer.anchor_after(buffer_range.end),
};
if range.end > cursor.end(&()) {
cursor.next(&());
} else {
current_range = ranges.next();
}
Some(subrange_start_anchor..subrange_end_anchor)
})
}
/// Returns excerpts overlapping the given ranges. 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(
&self,
ranges: impl IntoIterator<Item = Range<Anchor>>,
@ -6707,4 +6760,119 @@ mod tests {
validate_excerpts(&excerpts, &expected_excerpts);
}
#[gpui::test]
fn test_split_ranges(cx: &mut AppContext) {
let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
multibuffer.update(cx, |multibuffer, cx| {
multibuffer.push_excerpts(
buffer_1.clone(),
[ExcerptRange {
context: 0..buffer_1.read(cx).len(),
primary: None,
}],
cx,
);
multibuffer.push_excerpts(
buffer_2.clone(),
[ExcerptRange {
context: 0..buffer_2.read(cx).len(),
primary: None,
}],
cx,
);
});
let snapshot = multibuffer.read(cx).snapshot(cx);
let buffer_1_len = buffer_1.read(cx).len();
let buffer_2_len = buffer_2.read(cx).len();
let buffer_1_midpoint = buffer_1_len / 2;
let buffer_2_start = buffer_1_len + '\n'.len_utf8();
let buffer_2_midpoint = buffer_2_start + buffer_2_len / 2;
let total_len = buffer_2_start + buffer_2_len;
let input_ranges = [
0..buffer_1_midpoint,
buffer_1_midpoint..buffer_2_midpoint,
buffer_2_midpoint..total_len,
]
.map(|range| snapshot.anchor_before(range.start)..snapshot.anchor_after(range.end));
let actual_ranges = snapshot
.split_ranges(input_ranges.into_iter())
.map(|range| range.to_offset(&snapshot))
.collect::<Vec<_>>();
let expected_ranges = vec![
0..buffer_1_midpoint,
buffer_1_midpoint..buffer_1_len,
buffer_2_start..buffer_2_midpoint,
buffer_2_midpoint..total_len,
];
assert_eq!(actual_ranges, expected_ranges);
}
#[gpui::test]
fn test_split_ranges_single_range_spanning_three_excerpts(cx: &mut AppContext) {
let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'm'), cx));
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
multibuffer.update(cx, |multibuffer, cx| {
multibuffer.push_excerpts(
buffer_1.clone(),
[ExcerptRange {
context: 0..buffer_1.read(cx).len(),
primary: None,
}],
cx,
);
multibuffer.push_excerpts(
buffer_2.clone(),
[ExcerptRange {
context: 0..buffer_2.read(cx).len(),
primary: None,
}],
cx,
);
multibuffer.push_excerpts(
buffer_3.clone(),
[ExcerptRange {
context: 0..buffer_3.read(cx).len(),
primary: None,
}],
cx,
);
});
let snapshot = multibuffer.read(cx).snapshot(cx);
let buffer_1_len = buffer_1.read(cx).len();
let buffer_2_len = buffer_2.read(cx).len();
let buffer_3_len = buffer_3.read(cx).len();
let buffer_2_start = buffer_1_len + '\n'.len_utf8();
let buffer_3_start = buffer_2_start + buffer_2_len + '\n'.len_utf8();
let buffer_1_midpoint = buffer_1_len / 2;
let buffer_3_midpoint = buffer_3_start + buffer_3_len / 2;
let input_range =
snapshot.anchor_before(buffer_1_midpoint)..snapshot.anchor_after(buffer_3_midpoint);
let actual_ranges = snapshot
.split_ranges(std::iter::once(input_range))
.map(|range| range.to_offset(&snapshot))
.collect::<Vec<_>>();
let expected_ranges = vec![
buffer_1_midpoint..buffer_1_len,
buffer_2_start..buffer_2_start + buffer_2_len,
buffer_3_start..buffer_3_midpoint,
];
assert_eq!(actual_ranges, expected_ranges);
}
}