WIP
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>(
|
||||
|
||||
Reference in New Issue
Block a user