This commit is contained in:
Antonio Scandurra
2024-09-17 14:58:32 -06:00
parent fcd27b0019
commit adcdc9cd89
4 changed files with 112 additions and 69 deletions

View File

@@ -739,8 +739,8 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
// Ensure we don't panic when navigation data contains invalid anchors *and* points.
let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
invalid_anchor.text_anchor = text::Anchor::Character {
invalid_anchor.text_anchor = text::Anchor::Start {
buffer_id: BufferId::new(999).unwrap(),
};
let invalid_point = Point::new(9999, 0);
editor.navigate(

View File

@@ -2646,7 +2646,7 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
for buffer in &buffers {
let buffer = buffer.read(cx).snapshot();
let actual_remote_selections = buffer
.selections_in_range(Anchor::Start..Anchor::End, false)
.selections_in_range(buffer.min_anchor()..buffer.max_anchor(), false)
.map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
.collect::<Vec<_>>();
let expected_remote_selections = active_selections

View File

@@ -6,11 +6,14 @@ use std::{cmp::Ordering, fmt::Debug, ops::Range};
use sum_tree::Bias;
/// A timestamped position in a buffer
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, Default)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub enum Anchor {
#[default]
Start,
End,
Start {
buffer_id: BufferId,
},
End {
buffer_id: BufferId,
},
Character {
buffer_id: BufferId,
insertion_id: clock::Lamport,
@@ -20,41 +23,49 @@ pub enum Anchor {
}
impl Anchor {
pub fn buffer_id(&self) -> BufferId {
match self {
Anchor::Start { buffer_id } => *buffer_id,
Anchor::End { buffer_id } => *buffer_id,
Anchor::Character { buffer_id, .. } => *buffer_id,
}
}
pub fn cmp(&self, other: &Anchor, buffer: &BufferSnapshot) -> Ordering {
debug_assert_eq!(
self.buffer_id(),
other.buffer_id(),
"anchors belong to different buffers"
);
match (self, other) {
(Anchor::Start, Anchor::Start) | (Anchor::End, Anchor::End) => Ordering::Equal,
(Anchor::Start, _) | (_, Anchor::End) => Ordering::Less,
(_, Anchor::Start) | (Anchor::End, _) => Ordering::Greater,
(Anchor::Start { .. }, Anchor::Start { .. }) => Ordering::Equal,
(Anchor::End { .. }, Anchor::End { .. }) => Ordering::Equal,
(Anchor::Start { .. }, _) | (_, Anchor::End { .. }) => Ordering::Less,
(_, Anchor::Start { .. }) | (Anchor::End { .. }, _) => Ordering::Greater,
(
Anchor::Character {
buffer_id,
insertion_id,
offset,
bias,
..
},
Anchor::Character {
buffer_id: other_buffer_id,
insertion_id: other_insertion_id,
offset: other_offset,
bias: other_bias,
..
},
) => {
debug_assert_eq!(
buffer_id, other_buffer_id,
"anchors belong to different buffers"
);
let fragment_id_comparison = if insertion_id == other_insertion_id {
Ordering::Equal
if insertion_id == other_insertion_id {
offset
.cmp(&other_offset)
.then_with(|| bias.cmp(&other_bias))
} else {
buffer
.fragment_id_for_anchor(self)
.cmp(buffer.fragment_id_for_anchor(other))
};
fragment_id_comparison
.then_with(|| offset.cmp(&other_offset))
.then_with(|| bias.cmp(&other_bias))
}
}
}
}
@@ -85,8 +96,10 @@ impl Anchor {
pub fn bias_left(&self, buffer: &BufferSnapshot) -> Anchor {
match self {
Anchor::Start => Anchor::Start,
Anchor::End => buffer.anchor_before(buffer.len()),
Anchor::Start { buffer_id } => Anchor::Start {
buffer_id: *buffer_id,
},
Anchor::End { .. } => buffer.anchor_before(buffer.len()),
Anchor::Character {
buffer_id,
insertion_id,
@@ -103,8 +116,10 @@ impl Anchor {
pub fn bias_right(&self, buffer: &BufferSnapshot) -> Anchor {
match self {
Anchor::Start => buffer.anchor_after(0),
Anchor::End => Anchor::End,
Anchor::Start { .. } => buffer.anchor_after(0),
Anchor::End { buffer_id } => Anchor::End {
buffer_id: *buffer_id,
},
Anchor::Character {
buffer_id,
insertion_id,
@@ -129,7 +144,9 @@ impl Anchor {
/// Returns true when the [`Anchor`] is located inside a visible fragment.
pub fn is_valid(&self, buffer: &BufferSnapshot) -> bool {
match self {
Anchor::Start | Anchor::End => true,
Anchor::Start { buffer_id } | Anchor::End { buffer_id } => {
*buffer_id == buffer.remote_id
}
Anchor::Character { buffer_id, .. } => {
if *buffer_id == buffer.remote_id {
let fragment_id = buffer.fragment_id_for_anchor(self);

View File

@@ -1914,6 +1914,18 @@ impl BufferSnapshot {
self.visible_text.summary()
}
pub fn min_anchor(&self) -> Anchor {
Anchor::Start {
buffer_id: self.remote_id,
}
}
pub fn max_anchor(&self) -> Anchor {
Anchor::End {
buffer_id: self.remote_id,
}
}
pub fn max_point(&self) -> Point {
self.visible_text.max_point()
}
@@ -2114,42 +2126,50 @@ impl BufferSnapshot {
let mut text_cursor = self.visible_text.cursor(0);
let mut position = D::default();
anchors.map(move |(anchor, payload)| match *anchor {
Anchor::Start => (D::default(), payload),
Anchor::End => (D::from_text_summary(&self.visible_text.summary()), payload),
Anchor::Character {
insertion_id,
offset,
bias,
..
} => {
let anchor_key = InsertionFragmentKey {
timestamp: insertion_id,
split_offset: offset,
};
insertion_cursor.seek(&anchor_key, bias, &());
if let Some(insertion) = insertion_cursor.item() {
let comparison = sum_tree::KeyedItem::key(insertion).cmp(&anchor_key);
if comparison == Ordering::Greater
|| (bias == Bias::Left && comparison == Ordering::Equal && offset > 0)
{
anchors.map(move |(anchor, payload)| {
debug_assert_eq!(
anchor.buffer_id(),
self.remote_id,
"anchor belongs to a different buffer"
);
match *anchor {
Anchor::Start { .. } => (D::default(), payload),
Anchor::End { .. } => (D::from_text_summary(&self.visible_text.summary()), payload),
Anchor::Character {
insertion_id,
offset,
bias,
..
} => {
let anchor_key = InsertionFragmentKey {
timestamp: insertion_id,
split_offset: offset,
};
insertion_cursor.seek(&anchor_key, bias, &());
if let Some(insertion) = insertion_cursor.item() {
let comparison = sum_tree::KeyedItem::key(insertion).cmp(&anchor_key);
if comparison == Ordering::Greater
|| (bias == Bias::Left && comparison == Ordering::Equal && offset > 0)
{
insertion_cursor.prev(&());
}
} else {
insertion_cursor.prev(&());
}
} else {
insertion_cursor.prev(&());
}
let insertion = insertion_cursor.item().expect("invalid insertion");
assert_eq!(insertion.timestamp, insertion_id, "invalid insertion");
let insertion = insertion_cursor.item().expect("invalid insertion");
assert_eq!(insertion.timestamp, insertion_id, "invalid insertion");
fragment_cursor.seek_forward(&Some(&insertion.fragment_id), Bias::Left, &None);
let fragment = fragment_cursor.item().unwrap();
let mut fragment_offset = fragment_cursor.start().1;
if fragment.visible {
fragment_offset += offset - insertion.split_offset;
}
fragment_cursor.seek_forward(&Some(&insertion.fragment_id), Bias::Left, &None);
let fragment = fragment_cursor.item().unwrap();
let mut fragment_offset = fragment_cursor.start().1;
if fragment.visible {
fragment_offset += offset - insertion.split_offset;
}
position.add_assign(&text_cursor.summary(fragment_offset));
(position.clone(), payload)
position.add_assign(&text_cursor.summary(fragment_offset));
(position.clone(), payload)
}
}
})
}
@@ -2162,9 +2182,15 @@ impl BufferSnapshot {
}
fn fragment_id_for_anchor(&self, anchor: &Anchor) -> &Locator {
debug_assert_eq!(
anchor.buffer_id(),
self.remote_id,
"anchor belongs to a different buffer"
);
match *anchor {
Anchor::Start => Locator::min_ref(),
Anchor::End => Locator::max_ref(),
Anchor::Start { .. } => Locator::min_ref(),
Anchor::End { .. } => Locator::max_ref(),
Anchor::Character {
insertion_id,
offset,
@@ -2220,9 +2246,9 @@ impl BufferSnapshot {
fn anchor_at_offset(&self, offset: usize, bias: Bias) -> Anchor {
if bias == Bias::Left && offset == 0 {
Anchor::Start
self.min_anchor()
} else if bias == Bias::Right && offset == self.len() {
Anchor::End
self.max_anchor()
} else {
let mut fragment_cursor = self.fragments.cursor::<usize>();
fragment_cursor.seek(&offset, bias, &None);
@@ -2239,8 +2265,8 @@ impl BufferSnapshot {
pub fn can_resolve(&self, anchor: &Anchor) -> bool {
match *anchor {
Anchor::Start => true,
Anchor::End => true,
Anchor::Start { buffer_id } => self.remote_id == buffer_id,
Anchor::End { buffer_id } => self.remote_id == buffer_id,
Anchor::Character {
buffer_id,
insertion_id,
@@ -2272,7 +2298,7 @@ impl BufferSnapshot {
where
D: TextDimension + Ord,
{
self.edits_since_in_range(since, Anchor::Start..Anchor::End)
self.edits_since_in_range(since, self.min_anchor()..self.max_anchor())
}
pub fn anchored_edits_since<'a, D>(
@@ -2282,7 +2308,7 @@ impl BufferSnapshot {
where
D: TextDimension + Ord,
{
self.anchored_edits_since_in_range(since, Anchor::Start..Anchor::End)
self.anchored_edits_since_in_range(since, self.min_anchor()..self.max_anchor())
}
pub fn edits_since_in_range<'a, D>(